Merge "Expose "default tts locale" to the TTS V2 API."
diff --git a/Android.mk b/Android.mk
index 2f3a990..c889fa1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -77,9 +77,9 @@
core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
core/java/android/app/IStopUserCallback.aidl \
- core/java/android/app/task/ITaskCallback.aidl \
- core/java/android/app/task/ITaskManager.aidl \
- core/java/android/app/task/ITaskService.aidl \
+ core/java/android/app/job/IJobCallback.aidl \
+ core/java/android/app/job/IJobScheduler.aidl \
+ core/java/android/app/job/IJobService.aidl \
core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
core/java/android/app/IUiAutomationConnection.aidl \
@@ -147,8 +147,6 @@
core/java/android/hardware/ISerialManager.aidl \
core/java/android/hardware/display/IDisplayManager.aidl \
core/java/android/hardware/display/IDisplayManagerCallback.aidl \
- core/java/android/hardware/hdmi/IHdmiCecListener.aidl \
- core/java/android/hardware/hdmi/IHdmiCecService.aidl \
core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \
core/java/android/hardware/hdmi/IHdmiControlService.aidl \
core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \
@@ -217,14 +215,6 @@
core/java/android/service/wallpaper/IWallpaperConnection.aidl \
core/java/android/service/wallpaper/IWallpaperEngine.aidl \
core/java/android/service/wallpaper/IWallpaperService.aidl \
- core/java/android/tv/ITvInputClient.aidl \
- core/java/android/tv/ITvInputHardware.aidl \
- core/java/android/tv/ITvInputHardwareCallback.aidl \
- core/java/android/tv/ITvInputManager.aidl \
- core/java/android/tv/ITvInputService.aidl \
- core/java/android/tv/ITvInputServiceCallback.aidl \
- core/java/android/tv/ITvInputSession.aidl \
- core/java/android/tv/ITvInputSessionCallback.aidl \
core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
core/java/android/view/accessibility/IAccessibilityManager.aidl \
@@ -240,6 +230,7 @@
core/java/android/view/IWindowId.aidl \
core/java/android/view/IWindowManager.aidl \
core/java/android/view/IWindowSession.aidl \
+ core/java/android/view/IWindowSessionCallback.aidl \
core/java/android/speech/IRecognitionListener.aidl \
core/java/android/speech/IRecognitionService.aidl \
core/java/android/speech/tts/ITextToSpeechCallback.aidl \
@@ -314,14 +305,23 @@
media/java/android/media/IRemoteVolumeObserver.aidl \
media/java/android/media/IRingtonePlayer.aidl \
media/java/android/media/IVolumeController.aidl \
- media/java/android/media/routeprovider/IRouteConnection.aidl \
- media/java/android/media/routeprovider/IRouteProvider.aidl \
- media/java/android/media/routeprovider/IRouteProviderCallback.aidl \
- media/java/android/media/session/ISessionController.aidl \
- media/java/android/media/session/ISessionControllerCallback.aidl \
- media/java/android/media/session/ISession.aidl \
- media/java/android/media/session/ISessionCallback.aidl \
- media/java/android/media/session/ISessionManager.aidl \
+ media/java/android/media/routeprovider/IRouteConnection.aidl \
+ media/java/android/media/routeprovider/IRouteProvider.aidl \
+ media/java/android/media/routeprovider/IRouteProviderCallback.aidl \
+ media/java/android/media/session/IActiveSessionsListener.aidl \
+ media/java/android/media/session/ISessionController.aidl \
+ media/java/android/media/session/ISessionControllerCallback.aidl \
+ media/java/android/media/session/ISession.aidl \
+ media/java/android/media/session/ISessionCallback.aidl \
+ media/java/android/media/session/ISessionManager.aidl \
+ media/java/android/media/tv/ITvInputClient.aidl \
+ media/java/android/media/tv/ITvInputHardware.aidl \
+ media/java/android/media/tv/ITvInputHardwareCallback.aidl \
+ media/java/android/media/tv/ITvInputManager.aidl \
+ media/java/android/media/tv/ITvInputService.aidl \
+ media/java/android/media/tv/ITvInputServiceCallback.aidl \
+ media/java/android/media/tv/ITvInputSession.aidl \
+ media/java/android/media/tv/ITvInputSessionCallback.aidl \
telecomm/java/com/android/internal/telecomm/ICallService.aidl \
telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl \
telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl \
@@ -717,7 +717,7 @@
LOCAL_DROIDDOC_OPTIONS:=\
$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_private_stubs_current_intermediates/src \
- -showAnnotation android.annotation.PrivateApi \
+ -showAnnotation android.annotation.SystemApi \
-nodocs
LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 48a20a4..5b027b3 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -192,7 +192,11 @@
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/ITv*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager)
diff --git a/api/current.txt b/api/current.txt
index 3e0c42d..a2d62b0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9,7 +9,6 @@
field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
- field public static final java.lang.String ACCESS_INPUT_FLINGER = "android.permission.ACCESS_INPUT_FLINGER";
field public static final java.lang.String ACCESS_LOCATION_EXTRA_COMMANDS = "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS";
field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
field public static final java.lang.String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
@@ -249,7 +248,7 @@
field public static final int actionBarTabBarStyle = 16843508; // 0x10102f4
field public static final int actionBarTabStyle = 16843507; // 0x10102f3
field public static final int actionBarTabTextStyle = 16843509; // 0x10102f5
- field public static final int actionBarTheme = 16843828; // 0x1010434
+ field public static final int actionBarTheme = 16843827; // 0x1010433
field public static final int actionBarWidgetTheme = 16843671; // 0x1010397
field public static final int actionButtonStyle = 16843480; // 0x10102d8
field public static final int actionDropDownStyle = 16843479; // 0x10102d7
@@ -266,7 +265,7 @@
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 = 16843848; // 0x1010448
+ field public static final int actionOverflowMenuStyle = 16843846; // 0x1010446
field public static final int actionProviderClass = 16843657; // 0x1010389
field public static final int actionViewClass = 16843516; // 0x10102fc
field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
@@ -312,7 +311,7 @@
field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b
field public static final int autoLink = 16842928; // 0x10100b0
field public static final int autoMirrored = 16843754; // 0x10103ea
- field public static final int autoRemoveFromRecents = 16843850; // 0x101044a
+ field public static final int autoRemoveFromRecents = 16843849; // 0x1010449
field public static final int autoStart = 16843445; // 0x10102b5
field public static final deprecated int autoText = 16843114; // 0x101016a
field public static final int autoUrlDetect = 16843404; // 0x101028c
@@ -385,23 +384,22 @@
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 = 16843833; // 0x1010439
+ field public static final int colorAccent = 16843831; // 0x1010437
field public static final int colorActivatedHighlight = 16843664; // 0x1010390
field public static final int colorBackground = 16842801; // 0x1010031
field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab
- field public static final int colorButtonNormal = 16843822; // 0x101042e
- field public static final int colorControlActivated = 16843821; // 0x101042d
- field public static final int colorControlHighlight = 16843823; // 0x101042f
- field public static final int colorControlNormal = 16843820; // 0x101042c
+ field public static final int colorButtonNormal = 16843821; // 0x101042d
+ field public static final int colorControlActivated = 16843820; // 0x101042c
+ field public static final int colorControlHighlight = 16843822; // 0x101042e
+ field public static final int colorControlNormal = 16843819; // 0x101042b
field public static final int colorFocusedHighlight = 16843663; // 0x101038f
field public static final int colorForeground = 16842800; // 0x1010030
field public static final int colorForegroundInverse = 16843270; // 0x1010206
field public static final int colorLongPressedHighlight = 16843662; // 0x101038e
field public static final int colorMultiSelectHighlight = 16843665; // 0x1010391
field public static final int colorPressedHighlight = 16843661; // 0x101038d
- field public static final int colorPrimary = 16843831; // 0x1010437
- field public static final int colorPrimaryDark = 16843832; // 0x1010438
- field public static final int colorPrimaryLight = 16843830; // 0x1010436
+ field public static final int colorPrimary = 16843829; // 0x1010435
+ field public static final int colorPrimaryDark = 16843830; // 0x1010436
field public static final int columnCount = 16843639; // 0x1010377
field public static final int columnDelay = 16843215; // 0x10101cf
field public static final int columnOrderPreserved = 16843640; // 0x1010378
@@ -416,10 +414,10 @@
field public static final int content = 16843355; // 0x101025b
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
- field public static final int contentInsetEnd = 16843863; // 0x1010457
- field public static final int contentInsetLeft = 16843864; // 0x1010458
- field public static final int contentInsetRight = 16843865; // 0x1010459
- field public static final int contentInsetStart = 16843862; // 0x1010456
+ field public static final int contentInsetEnd = 16843862; // 0x1010456
+ field public static final int contentInsetLeft = 16843863; // 0x1010457
+ field public static final int contentInsetRight = 16843864; // 0x1010458
+ field public static final int contentInsetStart = 16843861; // 0x1010455
field public static final int controlX1 = 16843798; // 0x1010416
field public static final int controlX2 = 16843800; // 0x1010418
field public static final int controlY1 = 16843799; // 0x1010417
@@ -464,7 +462,7 @@
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 = 16843849; // 0x1010449
+ field public static final int documentLaunchMode = 16843847; // 0x1010447
field public static final int drawSelectorOnTop = 16843004; // 0x10100fc
field public static final int drawable = 16843161; // 0x1010199
field public static final int drawableBottom = 16843118; // 0x101016e
@@ -493,7 +491,8 @@
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 = 16843844; // 0x1010444
+ field public static final int elegantTextHeight = 16843871; // 0x101045f
+ field public static final int elevation = 16843842; // 0x1010442
field public static final int ellipsize = 16842923; // 0x10100ab
field public static final int ems = 16843096; // 0x1010158
field public static final int enabled = 16842766; // 0x101000e
@@ -503,10 +502,10 @@
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 = 16843846; // 0x1010446
+ field public static final int excludeClass = 16843844; // 0x1010444
field public static final int excludeFromRecents = 16842775; // 0x1010017
- field public static final int excludeId = 16843845; // 0x1010445
- field public static final int excludeViewName = 16843857; // 0x1010451
+ field public static final int excludeId = 16843843; // 0x1010443
+ field public static final int excludeViewName = 16843856; // 0x1010450
field public static final int exitFadeDuration = 16843533; // 0x101030d
field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052
field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053
@@ -568,7 +567,7 @@
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
- field public static final int fromId = 16843853; // 0x101044d
+ field public static final int fromId = 16843852; // 0x101044c
field public static final int fromScene = 16843741; // 0x10103dd
field public static final int fromXDelta = 16843206; // 0x10101c6
field public static final int fromXScale = 16843202; // 0x10101c2
@@ -601,7 +600,7 @@
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 = 16843847; // 0x1010447
+ field public static final int hideOnContentScroll = 16843845; // 0x1010445
field public static final int hint = 16843088; // 0x1010150
field public static final int homeAsUpIndicator = 16843531; // 0x101030b
field public static final int homeLayout = 16843549; // 0x101031d
@@ -707,8 +706,6 @@
field public static final int l_resource_pad23 = 16843770; // 0x10103fa
field public static final int l_resource_pad24 = 16843769; // 0x10103f9
field public static final int l_resource_pad25 = 16843768; // 0x10103f8
- field public static final int l_resource_pad26 = 16843767; // 0x10103f7
- field public static final int l_resource_pad27 = 16843766; // 0x10103f6
field public static final int l_resource_pad3 = 16843790; // 0x101040e
field public static final int l_resource_pad4 = 16843789; // 0x101040d
field public static final int l_resource_pad5 = 16843788; // 0x101040c
@@ -749,7 +746,7 @@
field public static final int layout_centerVertical = 16843153; // 0x1010191
field public static final int layout_column = 16843084; // 0x101014c
field public static final int layout_columnSpan = 16843645; // 0x101037d
- field public static final int layout_columnWeight = 16843868; // 0x101045c
+ field public static final int layout_columnWeight = 16843867; // 0x101045b
field public static final int layout_gravity = 16842931; // 0x10100b3
field public static final int layout_height = 16842997; // 0x10100f5
field public static final int layout_margin = 16842998; // 0x10100f6
@@ -761,7 +758,7 @@
field public static final int layout_marginTop = 16843000; // 0x10100f8
field public static final int layout_row = 16843643; // 0x101037b
field public static final int layout_rowSpan = 16843644; // 0x101037c
- field public static final int layout_rowWeight = 16843867; // 0x101045b
+ field public static final int layout_rowWeight = 16843866; // 0x101045a
field public static final int layout_scale = 16843155; // 0x1010193
field public static final int layout_span = 16843085; // 0x101014d
field public static final int layout_toEndOf = 16843704; // 0x10103b8
@@ -800,7 +797,7 @@
field public static final int manageSpaceActivity = 16842756; // 0x1010004
field public static final int mapViewStyle = 16842890; // 0x101008a
field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
- field public static final int matchOrder = 16843858; // 0x1010452
+ field public static final int matchOrder = 16843857; // 0x1010451
field public static final int max = 16843062; // 0x1010136
field public static final int maxDate = 16843584; // 0x1010340
field public static final int maxEms = 16843095; // 0x1010157
@@ -809,6 +806,7 @@
field public static final int maxLength = 16843104; // 0x1010160
field public static final int maxLevel = 16843186; // 0x10101b2
field public static final int maxLines = 16843091; // 0x1010153
+ field public static final int maxRecents = 16843848; // 0x1010448
field public static final int maxRows = 16843059; // 0x1010133
field public static final int maxSdkVersion = 16843377; // 0x1010271
field public static final int maxWidth = 16843039; // 0x101011f
@@ -833,10 +831,10 @@
field public static final int moreIcon = 16843061; // 0x1010135
field public static final int multiprocess = 16842771; // 0x1010013
field public static final int name = 16842755; // 0x1010003
- field public static final int navigationBarColor = 16843861; // 0x1010455
+ field public static final int navigationBarColor = 16843860; // 0x1010454
field public static final int navigationMode = 16843471; // 0x10102cf
field public static final int negativeButtonText = 16843254; // 0x10101f6
- field public static final int nestedScrollingEnabled = 16843834; // 0x101043a
+ field public static final int nestedScrollingEnabled = 16843832; // 0x1010438
field public static final int nextFocusDown = 16842980; // 0x10100e4
field public static final int nextFocusForward = 16843580; // 0x101033c
field public static final int nextFocusLeft = 16842977; // 0x10100e1
@@ -861,13 +859,14 @@
field public static final int overScrollFooter = 16843459; // 0x10102c3
field public static final int overScrollHeader = 16843458; // 0x10102c2
field public static final int overScrollMode = 16843457; // 0x10102c1
+ field public static final int overlapAnchor = 16843876; // 0x1010464
field public static final int overridesImplicitlyEnabledSubtype = 16843682; // 0x10103a2
field public static final int packageNames = 16843649; // 0x1010381
field public static final int padding = 16842965; // 0x10100d5
field public static final int paddingBottom = 16842969; // 0x10100d9
field public static final int paddingEnd = 16843700; // 0x10103b4
field public static final int paddingLeft = 16842966; // 0x10100d6
- field public static final int paddingMode = 16843866; // 0x101045a
+ field public static final int paddingMode = 16843865; // 0x1010459
field public static final int paddingRight = 16842968; // 0x10100d8
field public static final int paddingStart = 16843699; // 0x10103b3
field public static final int paddingTop = 16842967; // 0x10100d7
@@ -886,11 +885,10 @@
field public static final int permissionFlags = 16843719; // 0x10103c7
field public static final int permissionGroup = 16842762; // 0x101000a
field public static final int permissionGroupFlags = 16843717; // 0x10103c5
- field public static final int persistable = 16843824; // 0x1010430
+ field public static final int persistable = 16843823; // 0x101042f
field public static final int persistent = 16842765; // 0x101000d
field public static final int persistentDrawingCache = 16842990; // 0x10100ee
field public static final deprecated int phoneNumber = 16843111; // 0x1010167
- field public static final int pinned = 16843819; // 0x101042b
field public static final int pivotX = 16843189; // 0x10101b5
field public static final int pivotY = 16843190; // 0x10101b6
field public static final int popupAnimationStyle = 16843465; // 0x10102c9
@@ -963,7 +961,7 @@
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
field public static final int restrictedAccountType = 16843733; // 0x10103d5
- field public static final int reversible = 16843854; // 0x101044e
+ field public static final int reversible = 16843853; // 0x101044d
field public static final int right = 16843183; // 0x10101af
field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
field public static final int ringtoneType = 16843257; // 0x10101f9
@@ -1003,6 +1001,9 @@
field public static final int scrollbars = 16842974; // 0x10100de
field public static final int scrollingCache = 16843006; // 0x10100fe
field public static final deprecated int searchButtonText = 16843269; // 0x1010205
+ field public static final int searchKeyphrase = 16843873; // 0x1010461
+ field public static final int searchKeyphraseId = 16843872; // 0x1010460
+ field public static final int searchKeyphraseSupportedLocales = 16843874; // 0x1010462
field public static final int searchMode = 16843221; // 0x10101d5
field public static final int searchSettingsDescription = 16843402; // 0x101028a
field public static final int searchSuggestAuthority = 16843222; // 0x10101d6
@@ -1017,11 +1018,12 @@
field public static final int selectAllOnFocus = 16843102; // 0x101015e
field public static final int selectable = 16843238; // 0x10101e6
field public static final int selectableItemBackground = 16843534; // 0x101030e
- field public static final int selectableItemBackgroundBorderless = 16843871; // 0x101045f
+ field public static final int selectableItemBackgroundBorderless = 16843870; // 0x101045e
field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
- field public static final int sessionService = 16843841; // 0x1010441
+ field public static final int sessionService = 16843839; // 0x101043f
field public static final int settingsActivity = 16843301; // 0x1010225
+ field public static final int setupActivity = 16843766; // 0x10103f6
field public static final int shadowColor = 16843105; // 0x1010161
field public static final int shadowDx = 16843106; // 0x1010162
field public static final int shadowDy = 16843107; // 0x1010163
@@ -1041,7 +1043,7 @@
field public static final int shrinkColumns = 16843082; // 0x101014a
field public static final deprecated int singleLine = 16843101; // 0x101015d
field public static final int singleUser = 16843711; // 0x10103bf
- field public static final int slideEdge = 16843827; // 0x1010433
+ field public static final int slideEdge = 16843826; // 0x1010432
field public static final int smallIcon = 16843422; // 0x101029e
field public static final int smallScreens = 16843396; // 0x1010284
field public static final int smoothScrollbar = 16843313; // 0x1010231
@@ -1053,19 +1055,19 @@
field public static final int spinnerStyle = 16842881; // 0x1010081
field public static final int spinnersShown = 16843595; // 0x101034b
field public static final int splitMotionEvents = 16843503; // 0x10102ef
- field public static final int splitTrack = 16843855; // 0x101044f
+ field public static final int splitTrack = 16843854; // 0x101044e
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843747; // 0x10103e3
field public static final int sspPattern = 16843749; // 0x10103e5
field public static final int sspPrefix = 16843748; // 0x10103e4
field public static final int stackFromBottom = 16843005; // 0x10100fd
- field public static final int stackViewStyle = 16843842; // 0x1010442
+ field public static final int stackViewStyle = 16843840; // 0x1010440
field public static final int starStyle = 16842882; // 0x1010082
field public static final int startColor = 16843165; // 0x101019d
field public static final int startDelay = 16843746; // 0x10103e2
field public static final int startOffset = 16843198; // 0x10101be
field public static final deprecated int startYear = 16843132; // 0x101017c
- field public static final int stateListAnimator = 16843851; // 0x101044b
+ field public static final int stateListAnimator = 16843850; // 0x101044a
field public static final int stateNotNeeded = 16842774; // 0x1010016
field public static final int state_above_anchor = 16842922; // 0x10100aa
field public static final int state_accelerated = 16843547; // 0x101031b
@@ -1090,7 +1092,7 @@
field public static final int state_single = 16842915; // 0x10100a3
field public static final int state_window_focused = 16842909; // 0x101009d
field public static final int staticWallpaperPreview = 16843569; // 0x1010331
- field public static final int statusBarColor = 16843860; // 0x1010454
+ field public static final int statusBarColor = 16843859; // 0x1010453
field public static final int stepSize = 16843078; // 0x1010146
field public static final int stopWithTask = 16843626; // 0x101036a
field public static final int streamType = 16843273; // 0x1010209
@@ -1102,7 +1104,7 @@
field public static final int strokeOpacity = 16843810; // 0x1010422
field public static final int strokeWidth = 16843811; // 0x1010423
field public static final int subtitle = 16843473; // 0x10102d1
- field public static final int subtitleTextAppearance = 16843826; // 0x1010432
+ field public static final int subtitleTextAppearance = 16843825; // 0x1010431
field public static final int subtitleTextStyle = 16843513; // 0x10102f9
field public static final int subtypeExtraValue = 16843674; // 0x101039a
field public static final int subtypeId = 16843713; // 0x10103c1
@@ -1119,7 +1121,7 @@
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 = 16843843; // 0x1010443
+ field public static final int switchStyle = 16843841; // 0x1010441
field public static final int switchTextAppearance = 16843630; // 0x101036e
field public static final int switchTextOff = 16843628; // 0x101036c
field public static final int switchTextOn = 16843627; // 0x101036b
@@ -1135,7 +1137,7 @@
field public static final int targetId = 16843740; // 0x10103dc
field public static final int targetPackage = 16842785; // 0x1010021
field public static final int targetSdkVersion = 16843376; // 0x1010270
- field public static final int targetViewName = 16843856; // 0x1010450
+ field public static final int targetViewName = 16843855; // 0x101044f
field public static final int taskAffinity = 16842770; // 0x1010012
field public static final int taskCloseEnterAnimation = 16842942; // 0x10100be
field public static final int taskCloseExitAnimation = 16842943; // 0x10100bf
@@ -1157,7 +1159,7 @@
field public static final int textAppearanceLargeInverse = 16842819; // 0x1010043
field public static final int textAppearanceLargePopupMenu = 16843521; // 0x1010301
field public static final int textAppearanceListItem = 16843678; // 0x101039e
- field public static final int textAppearanceListItemSecondary = 16843829; // 0x1010435
+ field public static final int textAppearanceListItemSecondary = 16843828; // 0x1010434
field public static final int textAppearanceListItemSmall = 16843679; // 0x101039f
field public static final int textAppearanceMedium = 16842817; // 0x1010041
field public static final int textAppearanceMediumInverse = 16842820; // 0x1010044
@@ -1221,11 +1223,11 @@
field public static final int tintMode = 16843797; // 0x1010415
field public static final int title = 16843233; // 0x10101e1
field public static final int titleCondensed = 16843234; // 0x10101e2
- field public static final int titleTextAppearance = 16843825; // 0x1010431
+ field public static final int titleTextAppearance = 16843824; // 0x1010430
field public static final int titleTextStyle = 16843512; // 0x10102f8
field public static final int toAlpha = 16843211; // 0x10101cb
field public static final int toDegrees = 16843188; // 0x10101b4
- field public static final int toId = 16843852; // 0x101044c
+ field public static final int toId = 16843851; // 0x101044b
field public static final int toScene = 16843742; // 0x10103de
field public static final int toXDelta = 16843207; // 0x10101c7
field public static final int toXScale = 16843203; // 0x10101c3
@@ -1244,14 +1246,15 @@
field public static final int transition = 16843743; // 0x10103df
field public static final int transitionGroup = 16843803; // 0x101041b
field public static final int transitionOrdering = 16843744; // 0x10103e0
- field public static final int translateX = 16843869; // 0x101045d
- field public static final int translateY = 16843870; // 0x101045e
+ field public static final int translateX = 16843868; // 0x101045c
+ field public static final int translateY = 16843869; // 0x101045d
field public static final int translationX = 16843554; // 0x1010322
field public static final int translationY = 16843555; // 0x1010323
field public static final int translationZ = 16843796; // 0x1010414
field public static final int trimPathEnd = 16843813; // 0x1010425
field public static final int trimPathOffset = 16843814; // 0x1010426
field public static final int trimPathStart = 16843812; // 0x1010424
+ field public static final int tvInputType = 16843767; // 0x10103f7
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -1307,8 +1310,8 @@
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 = 16843840; // 0x1010440
- field public static final int windowAllowExitTransitionOverlap = 16843839; // 0x101043f
+ field public static final int windowAllowEnterTransitionOverlap = 16843838; // 0x101043e
+ field public static final int windowAllowExitTransitionOverlap = 16843837; // 0x101043d
field public static final int windowAnimationStyle = 16842926; // 0x10100ae
field public static final int windowBackground = 16842836; // 0x1010054
field public static final int windowCloseOnTouchOutside = 16843611; // 0x101035b
@@ -1316,12 +1319,12 @@
field public static final int windowContentTransitionManager = 16843795; // 0x1010413
field public static final int windowContentTransitions = 16843794; // 0x1010412
field public static final int windowDisablePreview = 16843298; // 0x1010222
- field public static final int windowDrawsSystemBarBackgrounds = 16843859; // 0x1010453
+ field public static final int windowDrawsSystemBarBackgrounds = 16843858; // 0x1010452
field public static final int windowEnableSplitTouch = 16843543; // 0x1010317
field public static final int windowEnterAnimation = 16842932; // 0x10100b4
- field public static final int windowEnterTransition = 16843835; // 0x101043b
+ field public static final int windowEnterTransition = 16843833; // 0x1010439
field public static final int windowExitAnimation = 16842933; // 0x10100b5
- field public static final int windowExitTransition = 16843836; // 0x101043c
+ field public static final int windowExitTransition = 16843834; // 0x101043a
field public static final int windowFrame = 16842837; // 0x1010055
field public static final int windowFullscreen = 16843277; // 0x101020d
field public static final int windowHideAnimation = 16842935; // 0x10100b7
@@ -1332,8 +1335,8 @@
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 = 16843837; // 0x101043d
- field public static final int windowSharedElementExitTransition = 16843838; // 0x101043e
+ field public static final int windowSharedElementEnterTransition = 16843835; // 0x101043b
+ field public static final int windowSharedElementExitTransition = 16843836; // 0x101043c
field public static final int windowShowAnimation = 16842934; // 0x10100b6
field public static final int windowShowWallpaper = 16843410; // 0x1010292
field public static final int windowSoftInputMode = 16843307; // 0x101022b
@@ -1341,6 +1344,7 @@
field public static final int windowTitleBackgroundStyle = 16842844; // 0x101005c
field public static final int windowTitleSize = 16842842; // 0x101005a
field public static final int windowTitleStyle = 16842843; // 0x101005b
+ field public static final int windowTransitionBackgroundFadeDuration = 16843875; // 0x1010463
field public static final int windowTranslucentNavigation = 16843760; // 0x10103f0
field public static final int windowTranslucentStatus = 16843759; // 0x10103ef
field public static final int writePermission = 16842760; // 0x1010008
@@ -1865,54 +1869,54 @@
field public static final int TextAppearance_Inverse = 16973887; // 0x103003f
field public static final int TextAppearance_Large = 16973890; // 0x1030042
field public static final int TextAppearance_Large_Inverse = 16973891; // 0x1030043
+ field public static final int TextAppearance_Material = 16974350; // 0x103020e
+ field public static final int TextAppearance_Material_Body1 = 16974552; // 0x10302d8
+ field public static final int TextAppearance_Material_Body2 = 16974551; // 0x10302d7
+ field public static final int TextAppearance_Material_Button = 16974555; // 0x10302db
+ field public static final int TextAppearance_Material_Caption = 16974553; // 0x10302d9
+ field public static final int TextAppearance_Material_DialogWindowTitle = 16974351; // 0x103020f
+ field public static final int TextAppearance_Material_Display1 = 16974547; // 0x10302d3
+ field public static final int TextAppearance_Material_Display2 = 16974546; // 0x10302d2
+ field public static final int TextAppearance_Material_Display3 = 16974545; // 0x10302d1
+ field public static final int TextAppearance_Material_Display4 = 16974544; // 0x10302d0
+ field public static final int TextAppearance_Material_Headline = 16974548; // 0x10302d4
+ field public static final int TextAppearance_Material_Inverse = 16974352; // 0x1030210
+ field public static final int TextAppearance_Material_Large = 16974353; // 0x1030211
+ field public static final int TextAppearance_Material_Large_Inverse = 16974354; // 0x1030212
+ field public static final int TextAppearance_Material_Medium = 16974355; // 0x1030213
+ field public static final int TextAppearance_Material_Medium_Inverse = 16974356; // 0x1030214
+ field public static final int TextAppearance_Material_Menu = 16974554; // 0x10302da
+ field public static final int TextAppearance_Material_SearchResult_Subtitle = 16974357; // 0x1030215
+ field public static final int TextAppearance_Material_SearchResult_Title = 16974358; // 0x1030216
+ field public static final int TextAppearance_Material_Small = 16974359; // 0x1030217
+ field public static final int TextAppearance_Material_Small_Inverse = 16974360; // 0x1030218
+ field public static final int TextAppearance_Material_Subhead = 16974550; // 0x10302d6
+ field public static final int TextAppearance_Material_Title = 16974549; // 0x10302d5
+ field public static final int TextAppearance_Material_Widget = 16974362; // 0x103021a
+ field public static final int TextAppearance_Material_Widget_ActionBar_Menu = 16974363; // 0x103021b
+ field public static final int TextAppearance_Material_Widget_ActionBar_Subtitle = 16974364; // 0x103021c
+ field public static final int TextAppearance_Material_Widget_ActionBar_Subtitle_Inverse = 16974365; // 0x103021d
+ field public static final int TextAppearance_Material_Widget_ActionBar_Title = 16974366; // 0x103021e
+ field public static final int TextAppearance_Material_Widget_ActionBar_Title_Inverse = 16974367; // 0x103021f
+ field public static final int TextAppearance_Material_Widget_ActionMode_Subtitle = 16974368; // 0x1030220
+ field public static final int TextAppearance_Material_Widget_ActionMode_Subtitle_Inverse = 16974369; // 0x1030221
+ field public static final int TextAppearance_Material_Widget_ActionMode_Title = 16974370; // 0x1030222
+ field public static final int TextAppearance_Material_Widget_ActionMode_Title_Inverse = 16974371; // 0x1030223
+ field public static final int TextAppearance_Material_Widget_Button = 16974372; // 0x1030224
+ field public static final int TextAppearance_Material_Widget_DropDownHint = 16974373; // 0x1030225
+ field public static final int TextAppearance_Material_Widget_DropDownItem = 16974374; // 0x1030226
+ field public static final int TextAppearance_Material_Widget_EditText = 16974375; // 0x1030227
+ field public static final int TextAppearance_Material_Widget_IconMenu_Item = 16974376; // 0x1030228
+ field public static final int TextAppearance_Material_Widget_PopupMenu = 16974377; // 0x1030229
+ field public static final int TextAppearance_Material_Widget_PopupMenu_Large = 16974378; // 0x103022a
+ field public static final int TextAppearance_Material_Widget_PopupMenu_Small = 16974379; // 0x103022b
+ field public static final int TextAppearance_Material_Widget_TabWidget = 16974380; // 0x103022c
+ field public static final int TextAppearance_Material_Widget_TextView = 16974381; // 0x103022d
+ field public static final int TextAppearance_Material_Widget_TextView_PopupMenu = 16974382; // 0x103022e
+ field public static final int TextAppearance_Material_Widget_TextView_SpinnerItem = 16974383; // 0x103022f
+ field public static final int TextAppearance_Material_WindowTitle = 16974361; // 0x1030219
field public static final int TextAppearance_Medium = 16973892; // 0x1030044
field public static final int TextAppearance_Medium_Inverse = 16973893; // 0x1030045
- field public static final int TextAppearance_Quantum = 16974348; // 0x103020c
- field public static final int TextAppearance_Quantum_Body1 = 16974545; // 0x10302d1
- field public static final int TextAppearance_Quantum_Body2 = 16974544; // 0x10302d0
- field public static final int TextAppearance_Quantum_Button = 16974548; // 0x10302d4
- field public static final int TextAppearance_Quantum_Caption = 16974546; // 0x10302d2
- field public static final int TextAppearance_Quantum_DialogWindowTitle = 16974349; // 0x103020d
- field public static final int TextAppearance_Quantum_Display1 = 16974540; // 0x10302cc
- field public static final int TextAppearance_Quantum_Display2 = 16974539; // 0x10302cb
- field public static final int TextAppearance_Quantum_Display3 = 16974538; // 0x10302ca
- field public static final int TextAppearance_Quantum_Display4 = 16974537; // 0x10302c9
- field public static final int TextAppearance_Quantum_Headline = 16974541; // 0x10302cd
- field public static final int TextAppearance_Quantum_Inverse = 16974350; // 0x103020e
- field public static final int TextAppearance_Quantum_Large = 16974351; // 0x103020f
- field public static final int TextAppearance_Quantum_Large_Inverse = 16974352; // 0x1030210
- field public static final int TextAppearance_Quantum_Medium = 16974353; // 0x1030211
- field public static final int TextAppearance_Quantum_Medium_Inverse = 16974354; // 0x1030212
- field public static final int TextAppearance_Quantum_Menu = 16974547; // 0x10302d3
- field public static final int TextAppearance_Quantum_SearchResult_Subtitle = 16974355; // 0x1030213
- field public static final int TextAppearance_Quantum_SearchResult_Title = 16974356; // 0x1030214
- field public static final int TextAppearance_Quantum_Small = 16974357; // 0x1030215
- field public static final int TextAppearance_Quantum_Small_Inverse = 16974358; // 0x1030216
- field public static final int TextAppearance_Quantum_Subhead = 16974543; // 0x10302cf
- field public static final int TextAppearance_Quantum_Title = 16974542; // 0x10302ce
- field public static final int TextAppearance_Quantum_Widget = 16974360; // 0x1030218
- field public static final int TextAppearance_Quantum_Widget_ActionBar_Menu = 16974361; // 0x1030219
- field public static final int TextAppearance_Quantum_Widget_ActionBar_Subtitle = 16974362; // 0x103021a
- field public static final int TextAppearance_Quantum_Widget_ActionBar_Subtitle_Inverse = 16974363; // 0x103021b
- field public static final int TextAppearance_Quantum_Widget_ActionBar_Title = 16974364; // 0x103021c
- field public static final int TextAppearance_Quantum_Widget_ActionBar_Title_Inverse = 16974365; // 0x103021d
- field public static final int TextAppearance_Quantum_Widget_ActionMode_Subtitle = 16974366; // 0x103021e
- field public static final int TextAppearance_Quantum_Widget_ActionMode_Subtitle_Inverse = 16974367; // 0x103021f
- field public static final int TextAppearance_Quantum_Widget_ActionMode_Title = 16974368; // 0x1030220
- field public static final int TextAppearance_Quantum_Widget_ActionMode_Title_Inverse = 16974369; // 0x1030221
- field public static final int TextAppearance_Quantum_Widget_Button = 16974370; // 0x1030222
- field public static final int TextAppearance_Quantum_Widget_DropDownHint = 16974371; // 0x1030223
- field public static final int TextAppearance_Quantum_Widget_DropDownItem = 16974372; // 0x1030224
- field public static final int TextAppearance_Quantum_Widget_EditText = 16974373; // 0x1030225
- field public static final int TextAppearance_Quantum_Widget_IconMenu_Item = 16974374; // 0x1030226
- field public static final int TextAppearance_Quantum_Widget_PopupMenu = 16974375; // 0x1030227
- field public static final int TextAppearance_Quantum_Widget_PopupMenu_Large = 16974376; // 0x1030228
- field public static final int TextAppearance_Quantum_Widget_PopupMenu_Small = 16974377; // 0x1030229
- field public static final int TextAppearance_Quantum_Widget_TabWidget = 16974378; // 0x103022a
- field public static final int TextAppearance_Quantum_Widget_TextView = 16974379; // 0x103022b
- field public static final int TextAppearance_Quantum_Widget_TextView_PopupMenu = 16974380; // 0x103022c
- field public static final int TextAppearance_Quantum_Widget_TextView_SpinnerItem = 16974381; // 0x103022d
- field public static final int TextAppearance_Quantum_WindowTitle = 16974359; // 0x1030217
field public static final int TextAppearance_Small = 16973894; // 0x1030046
field public static final int TextAppearance_Small_Inverse = 16973895; // 0x1030047
field public static final int TextAppearance_StatusBar_EventContent = 16973927; // 0x1030067
@@ -1936,11 +1940,12 @@
field public static final int TextAppearance_Widget_TextView_SpinnerItem = 16973906; // 0x1030052
field public static final int TextAppearance_WindowTitle = 16973907; // 0x1030053
field public static final int Theme = 16973829; // 0x1030005
- field public static final int ThemeOverlay = 16974412; // 0x103024c
- field public static final int ThemeOverlay_Quantum = 16974413; // 0x103024d
- field public static final int ThemeOverlay_Quantum_ActionBarWidget = 16974416; // 0x1030250
- field public static final int ThemeOverlay_Quantum_Dark = 16974415; // 0x103024f
- field public static final int ThemeOverlay_Quantum_Light = 16974414; // 0x103024e
+ field public static final int ThemeOverlay = 16974416; // 0x1030250
+ field public static final int ThemeOverlay_Material = 16974417; // 0x1030251
+ field public static final int ThemeOverlay_Material_ActionBar = 16974418; // 0x1030252
+ field public static final int ThemeOverlay_Material_Dark = 16974420; // 0x1030254
+ field public static final int ThemeOverlay_Material_Dark_ActionBar = 16974421; // 0x1030255
+ field public static final int ThemeOverlay_Material_Light = 16974419; // 0x1030253
field public static final int Theme_Black = 16973832; // 0x1030008
field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
@@ -1970,6 +1975,7 @@
field public static final int Theme_DeviceDefault_NoActionBar_Overscan = 16974303; // 0x10301df
field public static final int Theme_DeviceDefault_NoActionBar_TranslucentDecor = 16974307; // 0x10301e3
field public static final int Theme_DeviceDefault_Panel = 16974138; // 0x103013a
+ field public static final int Theme_DeviceDefault_Settings = 16974384; // 0x1030230
field public static final int Theme_DeviceDefault_Wallpaper = 16974140; // 0x103013c
field public static final int Theme_DeviceDefault_Wallpaper_NoTitleBar = 16974141; // 0x103013d
field public static final int Theme_Dialog = 16973835; // 0x103000b
@@ -2007,41 +2013,42 @@
field public static final int Theme_Light_NoTitleBar_Fullscreen = 16973838; // 0x103000e
field public static final int Theme_Light_Panel = 16973914; // 0x103005a
field public static final int Theme_Light_WallpaperSettings = 16973922; // 0x1030062
+ field public static final int Theme_Material = 16974385; // 0x1030231
+ field public static final int Theme_Material_Dialog = 16974386; // 0x1030232
+ field public static final int Theme_Material_DialogWhenLarge = 16974390; // 0x1030236
+ field public static final int Theme_Material_DialogWhenLarge_NoActionBar = 16974391; // 0x1030237
+ field public static final int Theme_Material_Dialog_MinWidth = 16974387; // 0x1030233
+ field public static final int Theme_Material_Dialog_NoActionBar = 16974388; // 0x1030234
+ field public static final int Theme_Material_Dialog_NoActionBar_MinWidth = 16974389; // 0x1030235
+ field public static final int Theme_Material_InputMethod = 16974392; // 0x1030238
+ field public static final int Theme_Material_Light = 16974402; // 0x1030242
+ field public static final int Theme_Material_Light_DarkActionBar = 16974403; // 0x1030243
+ field public static final int Theme_Material_Light_Dialog = 16974404; // 0x1030244
+ field public static final int Theme_Material_Light_DialogWhenLarge = 16974408; // 0x1030248
+ field public static final int Theme_Material_Light_DialogWhenLarge_NoActionBar = 16974409; // 0x1030249
+ field public static final int Theme_Material_Light_Dialog_MinWidth = 16974405; // 0x1030245
+ field public static final int Theme_Material_Light_Dialog_NoActionBar = 16974406; // 0x1030246
+ field public static final int Theme_Material_Light_Dialog_NoActionBar_MinWidth = 16974407; // 0x1030247
+ field public static final int Theme_Material_Light_NoActionBar = 16974410; // 0x103024a
+ field public static final int Theme_Material_Light_NoActionBar_Fullscreen = 16974411; // 0x103024b
+ field public static final int Theme_Material_Light_NoActionBar_Overscan = 16974412; // 0x103024c
+ field public static final int Theme_Material_Light_NoActionBar_TranslucentDecor = 16974413; // 0x103024d
+ field public static final int Theme_Material_Light_Panel = 16974414; // 0x103024e
+ field public static final int Theme_Material_Light_Voice = 16974415; // 0x103024f
+ field public static final int Theme_Material_NoActionBar = 16974393; // 0x1030239
+ field public static final int Theme_Material_NoActionBar_Fullscreen = 16974394; // 0x103023a
+ field public static final int Theme_Material_NoActionBar_Overscan = 16974395; // 0x103023b
+ field public static final int Theme_Material_NoActionBar_TranslucentDecor = 16974396; // 0x103023c
+ field public static final int Theme_Material_Panel = 16974397; // 0x103023d
+ field public static final int Theme_Material_Settings = 16974398; // 0x103023e
+ field public static final int Theme_Material_Voice = 16974399; // 0x103023f
+ field public static final int Theme_Material_Wallpaper = 16974400; // 0x1030240
+ field public static final int Theme_Material_Wallpaper_NoTitleBar = 16974401; // 0x1030241
field public static final int Theme_NoDisplay = 16973909; // 0x1030055
field public static final int Theme_NoTitleBar = 16973830; // 0x1030006
field public static final int Theme_NoTitleBar_Fullscreen = 16973831; // 0x1030007
field public static final int Theme_NoTitleBar_OverlayActionModes = 16973930; // 0x103006a
field public static final int Theme_Panel = 16973913; // 0x1030059
- field public static final int Theme_Quantum = 16974382; // 0x103022e
- field public static final int Theme_Quantum_Dialog = 16974383; // 0x103022f
- field public static final int Theme_Quantum_DialogWhenLarge = 16974387; // 0x1030233
- field public static final int Theme_Quantum_DialogWhenLarge_NoActionBar = 16974388; // 0x1030234
- field public static final int Theme_Quantum_Dialog_MinWidth = 16974384; // 0x1030230
- field public static final int Theme_Quantum_Dialog_NoActionBar = 16974385; // 0x1030231
- field public static final int Theme_Quantum_Dialog_NoActionBar_MinWidth = 16974386; // 0x1030232
- field public static final int Theme_Quantum_InputMethod = 16974389; // 0x1030235
- field public static final int Theme_Quantum_Light = 16974398; // 0x103023e
- field public static final int Theme_Quantum_Light_DarkActionBar = 16974399; // 0x103023f
- field public static final int Theme_Quantum_Light_Dialog = 16974400; // 0x1030240
- field public static final int Theme_Quantum_Light_DialogWhenLarge = 16974404; // 0x1030244
- field public static final int Theme_Quantum_Light_DialogWhenLarge_NoActionBar = 16974405; // 0x1030245
- field public static final int Theme_Quantum_Light_Dialog_MinWidth = 16974401; // 0x1030241
- field public static final int Theme_Quantum_Light_Dialog_NoActionBar = 16974402; // 0x1030242
- field public static final int Theme_Quantum_Light_Dialog_NoActionBar_MinWidth = 16974403; // 0x1030243
- field public static final int Theme_Quantum_Light_NoActionBar = 16974406; // 0x1030246
- field public static final int Theme_Quantum_Light_NoActionBar_Fullscreen = 16974407; // 0x1030247
- field public static final int Theme_Quantum_Light_NoActionBar_Overscan = 16974408; // 0x1030248
- field public static final int Theme_Quantum_Light_NoActionBar_TranslucentDecor = 16974409; // 0x1030249
- field public static final int Theme_Quantum_Light_Panel = 16974410; // 0x103024a
- field public static final int Theme_Quantum_Light_Voice = 16974411; // 0x103024b
- field public static final int Theme_Quantum_NoActionBar = 16974390; // 0x1030236
- field public static final int Theme_Quantum_NoActionBar_Fullscreen = 16974391; // 0x1030237
- field public static final int Theme_Quantum_NoActionBar_Overscan = 16974392; // 0x1030238
- field public static final int Theme_Quantum_NoActionBar_TranslucentDecor = 16974393; // 0x1030239
- field public static final int Theme_Quantum_Panel = 16974394; // 0x103023a
- field public static final int Theme_Quantum_Voice = 16974395; // 0x103023b
- field public static final int Theme_Quantum_Wallpaper = 16974396; // 0x103023c
- field public static final int Theme_Quantum_Wallpaper_NoTitleBar = 16974397; // 0x103023d
field public static final int Theme_Translucent = 16973839; // 0x103000f
field public static final int Theme_Translucent_NoTitleBar = 16973840; // 0x1030010
field public static final int Theme_Translucent_NoTitleBar_Fullscreen = 16973841; // 0x1030011
@@ -2098,7 +2105,7 @@
field public static final int Widget_DeviceDefault_DropDownItem_Spinner = 16974178; // 0x1030162
field public static final int Widget_DeviceDefault_EditText = 16974154; // 0x103014a
field public static final int Widget_DeviceDefault_ExpandableListView = 16974155; // 0x103014b
- field public static final int Widget_DeviceDefault_FastScroll = 16974344; // 0x1030208
+ field public static final int Widget_DeviceDefault_FastScroll = 16974346; // 0x103020a
field public static final int Widget_DeviceDefault_GridView = 16974156; // 0x103014c
field public static final int Widget_DeviceDefault_HorizontalScrollView = 16974171; // 0x103015b
field public static final int Widget_DeviceDefault_ImageButton = 16974157; // 0x103014d
@@ -2132,7 +2139,7 @@
field public static final int Widget_DeviceDefault_Light_DropDownItem_Spinner = 16974233; // 0x1030199
field public static final int Widget_DeviceDefault_Light_EditText = 16974206; // 0x103017e
field public static final int Widget_DeviceDefault_Light_ExpandableListView = 16974207; // 0x103017f
- field public static final int Widget_DeviceDefault_Light_FastScroll = 16974346; // 0x103020a
+ field public static final int Widget_DeviceDefault_Light_FastScroll = 16974348; // 0x103020c
field public static final int Widget_DeviceDefault_Light_GridView = 16974208; // 0x1030180
field public static final int Widget_DeviceDefault_Light_HorizontalScrollView = 16974226; // 0x1030192
field public static final int Widget_DeviceDefault_Light_ImageButton = 16974209; // 0x1030181
@@ -2156,7 +2163,7 @@
field public static final int Widget_DeviceDefault_Light_ScrollView = 16974225; // 0x1030191
field public static final int Widget_DeviceDefault_Light_SeekBar = 16974220; // 0x103018c
field public static final int Widget_DeviceDefault_Light_Spinner = 16974227; // 0x1030193
- field public static final int Widget_DeviceDefault_Light_StackView = 16974347; // 0x103020b
+ field public static final int Widget_DeviceDefault_Light_StackView = 16974349; // 0x103020d
field public static final int Widget_DeviceDefault_Light_Tab = 16974237; // 0x103019d
field public static final int Widget_DeviceDefault_Light_TabWidget = 16974229; // 0x1030195
field public static final int Widget_DeviceDefault_Light_TextView = 16974202; // 0x103017a
@@ -2180,7 +2187,7 @@
field public static final int Widget_DeviceDefault_ScrollView = 16974170; // 0x103015a
field public static final int Widget_DeviceDefault_SeekBar = 16974165; // 0x1030155
field public static final int Widget_DeviceDefault_Spinner = 16974172; // 0x103015c
- field public static final int Widget_DeviceDefault_StackView = 16974345; // 0x1030209
+ field public static final int Widget_DeviceDefault_StackView = 16974347; // 0x103020b
field public static final int Widget_DeviceDefault_Tab = 16974189; // 0x103016d
field public static final int Widget_DeviceDefault_TabWidget = 16974174; // 0x103015e
field public static final int Widget_DeviceDefault_TextView = 16974150; // 0x1030146
@@ -2223,7 +2230,7 @@
field public static final int Widget_Holo_DropDownItem_Spinner = 16973995; // 0x10300ab
field public static final int Widget_Holo_EditText = 16973971; // 0x1030093
field public static final int Widget_Holo_ExpandableListView = 16973972; // 0x1030094
- field public static final int Widget_Holo_FastScroll = 16974339; // 0x1030203
+ field public static final int Widget_Holo_FastScroll = 16974341; // 0x1030205
field public static final int Widget_Holo_GridView = 16973973; // 0x1030095
field public static final int Widget_Holo_HorizontalScrollView = 16973988; // 0x10300a4
field public static final int Widget_Holo_ImageButton = 16973974; // 0x1030096
@@ -2244,7 +2251,7 @@
field public static final int Widget_Holo_Light_ActionMode_Inverse = 16974119; // 0x1030127
field public static final int Widget_Holo_Light_AutoCompleteTextView = 16974011; // 0x10300bb
field public static final int Widget_Holo_Light_Button = 16974006; // 0x10300b6
- field public static final int Widget_Holo_Light_Button_Borderless = 16974341; // 0x1030205
+ field public static final int Widget_Holo_Light_Button_Borderless = 16974343; // 0x1030207
field public static final int Widget_Holo_Light_Button_Borderless_Small = 16974107; // 0x103011b
field public static final int Widget_Holo_Light_Button_Inset = 16974008; // 0x10300b8
field public static final int Widget_Holo_Light_Button_Small = 16974007; // 0x10300b7
@@ -2258,7 +2265,7 @@
field public static final int Widget_Holo_Light_DropDownItem_Spinner = 16974041; // 0x10300d9
field public static final int Widget_Holo_Light_EditText = 16974014; // 0x10300be
field public static final int Widget_Holo_Light_ExpandableListView = 16974015; // 0x10300bf
- field public static final int Widget_Holo_Light_FastScroll = 16974342; // 0x1030206
+ field public static final int Widget_Holo_Light_FastScroll = 16974344; // 0x1030208
field public static final int Widget_Holo_Light_GridView = 16974016; // 0x10300c0
field public static final int Widget_Holo_Light_HorizontalScrollView = 16974034; // 0x10300d2
field public static final int Widget_Holo_Light_ImageButton = 16974017; // 0x10300c1
@@ -2282,7 +2289,7 @@
field public static final int Widget_Holo_Light_ScrollView = 16974033; // 0x10300d1
field public static final int Widget_Holo_Light_SeekBar = 16974028; // 0x10300cc
field public static final int Widget_Holo_Light_Spinner = 16974035; // 0x10300d3
- field public static final int Widget_Holo_Light_StackView = 16974343; // 0x1030207
+ field public static final int Widget_Holo_Light_StackView = 16974345; // 0x1030209
field public static final int Widget_Holo_Light_Tab = 16974052; // 0x10300e4
field public static final int Widget_Holo_Light_TabWidget = 16974037; // 0x10300d5
field public static final int Widget_Holo_Light_TextView = 16974010; // 0x10300ba
@@ -2306,7 +2313,7 @@
field public static final int Widget_Holo_ScrollView = 16973987; // 0x10300a3
field public static final int Widget_Holo_SeekBar = 16973982; // 0x103009e
field public static final int Widget_Holo_Spinner = 16973989; // 0x10300a5
- field public static final int Widget_Holo_StackView = 16974340; // 0x1030204
+ field public static final int Widget_Holo_StackView = 16974342; // 0x1030206
field public static final int Widget_Holo_Tab = 16974051; // 0x10300e3
field public static final int Widget_Holo_TabWidget = 16973991; // 0x10300a7
field public static final int Widget_Holo_TextView = 16973967; // 0x103008f
@@ -2321,6 +2328,128 @@
field public static final int Widget_ListView_DropDown = 16973872; // 0x1030030
field public static final int Widget_ListView_Menu = 16973873; // 0x1030031
field public static final int Widget_ListView_White = 16973871; // 0x103002f
+ field public static final int Widget_Material = 16974422; // 0x1030256
+ field public static final int Widget_Material_ActionBar = 16974423; // 0x1030257
+ field public static final int Widget_Material_ActionBar_Solid = 16974424; // 0x1030258
+ field public static final int Widget_Material_ActionBar_TabBar = 16974425; // 0x1030259
+ field public static final int Widget_Material_ActionBar_TabText = 16974426; // 0x103025a
+ field public static final int Widget_Material_ActionBar_TabView = 16974427; // 0x103025b
+ field public static final int Widget_Material_ActionButton = 16974428; // 0x103025c
+ field public static final int Widget_Material_ActionButton_CloseMode = 16974429; // 0x103025d
+ field public static final int Widget_Material_ActionButton_Overflow = 16974430; // 0x103025e
+ field public static final int Widget_Material_ActionMode = 16974431; // 0x103025f
+ field public static final int Widget_Material_AutoCompleteTextView = 16974432; // 0x1030260
+ field public static final int Widget_Material_Button = 16974433; // 0x1030261
+ field public static final int Widget_Material_ButtonBar = 16974439; // 0x1030267
+ field public static final int Widget_Material_ButtonBar_AlertDialog = 16974440; // 0x1030268
+ field public static final int Widget_Material_Button_Borderless = 16974434; // 0x1030262
+ field public static final int Widget_Material_Button_Borderless_Small = 16974435; // 0x1030263
+ field public static final int Widget_Material_Button_Inset = 16974436; // 0x1030264
+ field public static final int Widget_Material_Button_Small = 16974437; // 0x1030265
+ field public static final int Widget_Material_Button_Toggle = 16974438; // 0x1030266
+ field public static final int Widget_Material_CalendarView = 16974441; // 0x1030269
+ field public static final int Widget_Material_CheckedTextView = 16974442; // 0x103026a
+ field public static final int Widget_Material_CompoundButton_CheckBox = 16974443; // 0x103026b
+ field public static final int Widget_Material_CompoundButton_RadioButton = 16974444; // 0x103026c
+ field public static final int Widget_Material_CompoundButton_Star = 16974445; // 0x103026d
+ field public static final int Widget_Material_DatePicker = 16974446; // 0x103026e
+ field public static final int Widget_Material_DropDownItem = 16974447; // 0x103026f
+ field public static final int Widget_Material_DropDownItem_Spinner = 16974448; // 0x1030270
+ field public static final int Widget_Material_EditText = 16974449; // 0x1030271
+ field public static final int Widget_Material_ExpandableListView = 16974450; // 0x1030272
+ field public static final int Widget_Material_FastScroll = 16974451; // 0x1030273
+ field public static final int Widget_Material_GridView = 16974452; // 0x1030274
+ field public static final int Widget_Material_HorizontalScrollView = 16974453; // 0x1030275
+ field public static final int Widget_Material_ImageButton = 16974454; // 0x1030276
+ field public static final int Widget_Material_Light = 16974483; // 0x1030293
+ field public static final int Widget_Material_Light_ActionBar = 16974484; // 0x1030294
+ field public static final int Widget_Material_Light_ActionBar_Solid = 16974485; // 0x1030295
+ field public static final int Widget_Material_Light_ActionBar_TabBar = 16974486; // 0x1030296
+ field public static final int Widget_Material_Light_ActionBar_TabText = 16974487; // 0x1030297
+ field public static final int Widget_Material_Light_ActionBar_TabView = 16974488; // 0x1030298
+ field public static final int Widget_Material_Light_ActionButton = 16974489; // 0x1030299
+ field public static final int Widget_Material_Light_ActionButton_CloseMode = 16974490; // 0x103029a
+ field public static final int Widget_Material_Light_ActionButton_Overflow = 16974491; // 0x103029b
+ field public static final int Widget_Material_Light_ActionMode = 16974492; // 0x103029c
+ field public static final int Widget_Material_Light_AutoCompleteTextView = 16974493; // 0x103029d
+ field public static final int Widget_Material_Light_Button = 16974494; // 0x103029e
+ field public static final int Widget_Material_Light_ButtonBar = 16974500; // 0x10302a4
+ field public static final int Widget_Material_Light_ButtonBar_AlertDialog = 16974501; // 0x10302a5
+ field public static final int Widget_Material_Light_Button_Borderless = 16974495; // 0x103029f
+ field public static final int Widget_Material_Light_Button_Borderless_Small = 16974496; // 0x10302a0
+ field public static final int Widget_Material_Light_Button_Inset = 16974497; // 0x10302a1
+ field public static final int Widget_Material_Light_Button_Small = 16974498; // 0x10302a2
+ field public static final int Widget_Material_Light_Button_Toggle = 16974499; // 0x10302a3
+ field public static final int Widget_Material_Light_CalendarView = 16974502; // 0x10302a6
+ field public static final int Widget_Material_Light_CheckedTextView = 16974503; // 0x10302a7
+ field public static final int Widget_Material_Light_CompoundButton_CheckBox = 16974504; // 0x10302a8
+ field public static final int Widget_Material_Light_CompoundButton_RadioButton = 16974505; // 0x10302a9
+ field public static final int Widget_Material_Light_CompoundButton_Star = 16974506; // 0x10302aa
+ field public static final int Widget_Material_Light_DropDownItem = 16974507; // 0x10302ab
+ field public static final int Widget_Material_Light_DropDownItem_Spinner = 16974508; // 0x10302ac
+ field public static final int Widget_Material_Light_EditText = 16974509; // 0x10302ad
+ field public static final int Widget_Material_Light_ExpandableListView = 16974510; // 0x10302ae
+ field public static final int Widget_Material_Light_FastScroll = 16974511; // 0x10302af
+ field public static final int Widget_Material_Light_GridView = 16974512; // 0x10302b0
+ field public static final int Widget_Material_Light_HorizontalScrollView = 16974513; // 0x10302b1
+ field public static final int Widget_Material_Light_ImageButton = 16974514; // 0x10302b2
+ field public static final int Widget_Material_Light_ListPopupWindow = 16974515; // 0x10302b3
+ field public static final int Widget_Material_Light_ListView = 16974516; // 0x10302b4
+ field public static final int Widget_Material_Light_ListView_DropDown = 16974517; // 0x10302b5
+ field public static final int Widget_Material_Light_MediaRouteButton = 16974518; // 0x10302b6
+ field public static final int Widget_Material_Light_PopupMenu = 16974519; // 0x10302b7
+ field public static final int Widget_Material_Light_PopupMenu_Overflow = 16974520; // 0x10302b8
+ field public static final int Widget_Material_Light_PopupWindow = 16974521; // 0x10302b9
+ field public static final int Widget_Material_Light_ProgressBar = 16974522; // 0x10302ba
+ field public static final int Widget_Material_Light_ProgressBar_Horizontal = 16974523; // 0x10302bb
+ field public static final int Widget_Material_Light_ProgressBar_Inverse = 16974524; // 0x10302bc
+ field public static final int Widget_Material_Light_ProgressBar_Large = 16974525; // 0x10302bd
+ field public static final int Widget_Material_Light_ProgressBar_Large_Inverse = 16974526; // 0x10302be
+ field public static final int Widget_Material_Light_ProgressBar_Small = 16974527; // 0x10302bf
+ field public static final int Widget_Material_Light_ProgressBar_Small_Inverse = 16974528; // 0x10302c0
+ field public static final int Widget_Material_Light_ProgressBar_Small_Title = 16974529; // 0x10302c1
+ field public static final int Widget_Material_Light_RatingBar = 16974530; // 0x10302c2
+ field public static final int Widget_Material_Light_RatingBar_Indicator = 16974531; // 0x10302c3
+ field public static final int Widget_Material_Light_RatingBar_Small = 16974532; // 0x10302c4
+ field public static final int Widget_Material_Light_ScrollView = 16974533; // 0x10302c5
+ field public static final int Widget_Material_Light_SeekBar = 16974534; // 0x10302c6
+ field public static final int Widget_Material_Light_SegmentedButton = 16974535; // 0x10302c7
+ field public static final int Widget_Material_Light_Spinner = 16974537; // 0x10302c9
+ field public static final int Widget_Material_Light_StackView = 16974536; // 0x10302c8
+ field public static final int Widget_Material_Light_Tab = 16974538; // 0x10302ca
+ field public static final int Widget_Material_Light_TabWidget = 16974539; // 0x10302cb
+ field public static final int Widget_Material_Light_TextView = 16974540; // 0x10302cc
+ field public static final int Widget_Material_Light_TextView_SpinnerItem = 16974541; // 0x10302cd
+ field public static final int Widget_Material_Light_WebTextView = 16974542; // 0x10302ce
+ field public static final int Widget_Material_Light_WebView = 16974543; // 0x10302cf
+ field public static final int Widget_Material_ListPopupWindow = 16974455; // 0x1030277
+ field public static final int Widget_Material_ListView = 16974456; // 0x1030278
+ field public static final int Widget_Material_ListView_DropDown = 16974457; // 0x1030279
+ field public static final int Widget_Material_MediaRouteButton = 16974458; // 0x103027a
+ field public static final int Widget_Material_PopupMenu = 16974459; // 0x103027b
+ field public static final int Widget_Material_PopupMenu_Overflow = 16974460; // 0x103027c
+ field public static final int Widget_Material_PopupWindow = 16974461; // 0x103027d
+ field public static final int Widget_Material_ProgressBar = 16974462; // 0x103027e
+ field public static final int Widget_Material_ProgressBar_Horizontal = 16974463; // 0x103027f
+ field public static final int Widget_Material_ProgressBar_Large = 16974464; // 0x1030280
+ field public static final int Widget_Material_ProgressBar_Small = 16974465; // 0x1030281
+ field public static final int Widget_Material_ProgressBar_Small_Title = 16974466; // 0x1030282
+ field public static final int Widget_Material_RatingBar = 16974467; // 0x1030283
+ field public static final int Widget_Material_RatingBar_Indicator = 16974468; // 0x1030284
+ field public static final int Widget_Material_RatingBar_Small = 16974469; // 0x1030285
+ field public static final int Widget_Material_ScrollView = 16974470; // 0x1030286
+ field public static final int Widget_Material_SeekBar = 16974471; // 0x1030287
+ field public static final int Widget_Material_SegmentedButton = 16974472; // 0x1030288
+ field public static final int Widget_Material_Spinner = 16974474; // 0x103028a
+ field public static final int Widget_Material_StackView = 16974473; // 0x1030289
+ field public static final int Widget_Material_Tab = 16974475; // 0x103028b
+ field public static final int Widget_Material_TabWidget = 16974476; // 0x103028c
+ field public static final int Widget_Material_TextView = 16974477; // 0x103028d
+ field public static final int Widget_Material_TextView_SpinnerItem = 16974478; // 0x103028e
+ field public static final int Widget_Material_Toolbar = 16974479; // 0x103028f
+ field public static final int Widget_Material_Toolbar_Button_Navigation = 16974480; // 0x1030290
+ field public static final int Widget_Material_WebTextView = 16974481; // 0x1030291
+ field public static final int Widget_Material_WebView = 16974482; // 0x1030292
field public static final int Widget_PopupMenu = 16973958; // 0x1030086
field public static final int Widget_PopupWindow = 16973878; // 0x1030036
field public static final int Widget_ProgressBar = 16973852; // 0x103001c
@@ -2330,126 +2459,6 @@
field public static final int Widget_ProgressBar_Large_Inverse = 16973916; // 0x103005c
field public static final int Widget_ProgressBar_Small = 16973854; // 0x103001e
field public static final int Widget_ProgressBar_Small_Inverse = 16973917; // 0x103005d
- field public static final int Widget_Quantum = 16974417; // 0x1030251
- field public static final int Widget_Quantum_ActionBar = 16974418; // 0x1030252
- field public static final int Widget_Quantum_ActionBar_Solid = 16974419; // 0x1030253
- field public static final int Widget_Quantum_ActionBar_TabBar = 16974420; // 0x1030254
- field public static final int Widget_Quantum_ActionBar_TabText = 16974421; // 0x1030255
- field public static final int Widget_Quantum_ActionBar_TabView = 16974422; // 0x1030256
- field public static final int Widget_Quantum_ActionButton = 16974423; // 0x1030257
- field public static final int Widget_Quantum_ActionButton_CloseMode = 16974424; // 0x1030258
- field public static final int Widget_Quantum_ActionButton_Overflow = 16974425; // 0x1030259
- field public static final int Widget_Quantum_ActionMode = 16974426; // 0x103025a
- field public static final int Widget_Quantum_AutoCompleteTextView = 16974427; // 0x103025b
- field public static final int Widget_Quantum_Button = 16974428; // 0x103025c
- field public static final int Widget_Quantum_ButtonBar = 16974434; // 0x1030262
- field public static final int Widget_Quantum_ButtonBar_AlertDialog = 16974435; // 0x1030263
- field public static final int Widget_Quantum_Button_Borderless = 16974429; // 0x103025d
- field public static final int Widget_Quantum_Button_Borderless_Small = 16974430; // 0x103025e
- field public static final int Widget_Quantum_Button_Inset = 16974431; // 0x103025f
- field public static final int Widget_Quantum_Button_Small = 16974432; // 0x1030260
- field public static final int Widget_Quantum_Button_Toggle = 16974433; // 0x1030261
- field public static final int Widget_Quantum_CalendarView = 16974436; // 0x1030264
- field public static final int Widget_Quantum_CheckedTextView = 16974437; // 0x1030265
- field public static final int Widget_Quantum_CompoundButton_CheckBox = 16974438; // 0x1030266
- field public static final int Widget_Quantum_CompoundButton_RadioButton = 16974439; // 0x1030267
- field public static final int Widget_Quantum_CompoundButton_Star = 16974440; // 0x1030268
- field public static final int Widget_Quantum_DatePicker = 16974441; // 0x1030269
- field public static final int Widget_Quantum_DropDownItem = 16974442; // 0x103026a
- field public static final int Widget_Quantum_DropDownItem_Spinner = 16974443; // 0x103026b
- field public static final int Widget_Quantum_EditText = 16974444; // 0x103026c
- field public static final int Widget_Quantum_ExpandableListView = 16974445; // 0x103026d
- field public static final int Widget_Quantum_FastScroll = 16974446; // 0x103026e
- field public static final int Widget_Quantum_GridView = 16974447; // 0x103026f
- field public static final int Widget_Quantum_HorizontalScrollView = 16974448; // 0x1030270
- field public static final int Widget_Quantum_ImageButton = 16974449; // 0x1030271
- field public static final int Widget_Quantum_Light = 16974476; // 0x103028c
- field public static final int Widget_Quantum_Light_ActionBar = 16974477; // 0x103028d
- field public static final int Widget_Quantum_Light_ActionBar_Solid = 16974478; // 0x103028e
- field public static final int Widget_Quantum_Light_ActionBar_TabBar = 16974479; // 0x103028f
- field public static final int Widget_Quantum_Light_ActionBar_TabText = 16974480; // 0x1030290
- field public static final int Widget_Quantum_Light_ActionBar_TabView = 16974481; // 0x1030291
- field public static final int Widget_Quantum_Light_ActionButton = 16974482; // 0x1030292
- field public static final int Widget_Quantum_Light_ActionButton_CloseMode = 16974483; // 0x1030293
- field public static final int Widget_Quantum_Light_ActionButton_Overflow = 16974484; // 0x1030294
- field public static final int Widget_Quantum_Light_ActionMode = 16974485; // 0x1030295
- field public static final int Widget_Quantum_Light_AutoCompleteTextView = 16974486; // 0x1030296
- field public static final int Widget_Quantum_Light_Button = 16974487; // 0x1030297
- field public static final int Widget_Quantum_Light_ButtonBar = 16974493; // 0x103029d
- field public static final int Widget_Quantum_Light_ButtonBar_AlertDialog = 16974494; // 0x103029e
- field public static final int Widget_Quantum_Light_Button_Borderless = 16974488; // 0x1030298
- field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974489; // 0x1030299
- field public static final int Widget_Quantum_Light_Button_Inset = 16974490; // 0x103029a
- field public static final int Widget_Quantum_Light_Button_Small = 16974491; // 0x103029b
- field public static final int Widget_Quantum_Light_Button_Toggle = 16974492; // 0x103029c
- field public static final int Widget_Quantum_Light_CalendarView = 16974495; // 0x103029f
- field public static final int Widget_Quantum_Light_CheckedTextView = 16974496; // 0x10302a0
- field public static final int Widget_Quantum_Light_CompoundButton_CheckBox = 16974497; // 0x10302a1
- field public static final int Widget_Quantum_Light_CompoundButton_RadioButton = 16974498; // 0x10302a2
- field public static final int Widget_Quantum_Light_CompoundButton_Star = 16974499; // 0x10302a3
- field public static final int Widget_Quantum_Light_DropDownItem = 16974500; // 0x10302a4
- field public static final int Widget_Quantum_Light_DropDownItem_Spinner = 16974501; // 0x10302a5
- field public static final int Widget_Quantum_Light_EditText = 16974502; // 0x10302a6
- field public static final int Widget_Quantum_Light_ExpandableListView = 16974503; // 0x10302a7
- field public static final int Widget_Quantum_Light_FastScroll = 16974504; // 0x10302a8
- field public static final int Widget_Quantum_Light_GridView = 16974505; // 0x10302a9
- field public static final int Widget_Quantum_Light_HorizontalScrollView = 16974506; // 0x10302aa
- field public static final int Widget_Quantum_Light_ImageButton = 16974507; // 0x10302ab
- field public static final int Widget_Quantum_Light_ListPopupWindow = 16974508; // 0x10302ac
- field public static final int Widget_Quantum_Light_ListView = 16974509; // 0x10302ad
- field public static final int Widget_Quantum_Light_ListView_DropDown = 16974510; // 0x10302ae
- field public static final int Widget_Quantum_Light_MediaRouteButton = 16974511; // 0x10302af
- field public static final int Widget_Quantum_Light_PopupMenu = 16974512; // 0x10302b0
- field public static final int Widget_Quantum_Light_PopupMenu_Overflow = 16974513; // 0x10302b1
- field public static final int Widget_Quantum_Light_PopupWindow = 16974514; // 0x10302b2
- field public static final int Widget_Quantum_Light_ProgressBar = 16974515; // 0x10302b3
- field public static final int Widget_Quantum_Light_ProgressBar_Horizontal = 16974516; // 0x10302b4
- field public static final int Widget_Quantum_Light_ProgressBar_Inverse = 16974517; // 0x10302b5
- field public static final int Widget_Quantum_Light_ProgressBar_Large = 16974518; // 0x10302b6
- field public static final int Widget_Quantum_Light_ProgressBar_Large_Inverse = 16974519; // 0x10302b7
- field public static final int Widget_Quantum_Light_ProgressBar_Small = 16974520; // 0x10302b8
- field public static final int Widget_Quantum_Light_ProgressBar_Small_Inverse = 16974521; // 0x10302b9
- field public static final int Widget_Quantum_Light_ProgressBar_Small_Title = 16974522; // 0x10302ba
- field public static final int Widget_Quantum_Light_RatingBar = 16974523; // 0x10302bb
- field public static final int Widget_Quantum_Light_RatingBar_Indicator = 16974524; // 0x10302bc
- field public static final int Widget_Quantum_Light_RatingBar_Small = 16974525; // 0x10302bd
- field public static final int Widget_Quantum_Light_ScrollView = 16974526; // 0x10302be
- field public static final int Widget_Quantum_Light_SeekBar = 16974527; // 0x10302bf
- field public static final int Widget_Quantum_Light_SegmentedButton = 16974528; // 0x10302c0
- field public static final int Widget_Quantum_Light_Spinner = 16974530; // 0x10302c2
- field public static final int Widget_Quantum_Light_StackView = 16974529; // 0x10302c1
- field public static final int Widget_Quantum_Light_Tab = 16974531; // 0x10302c3
- field public static final int Widget_Quantum_Light_TabWidget = 16974532; // 0x10302c4
- field public static final int Widget_Quantum_Light_TextView = 16974533; // 0x10302c5
- field public static final int Widget_Quantum_Light_TextView_SpinnerItem = 16974534; // 0x10302c6
- field public static final int Widget_Quantum_Light_WebTextView = 16974535; // 0x10302c7
- field public static final int Widget_Quantum_Light_WebView = 16974536; // 0x10302c8
- field public static final int Widget_Quantum_ListPopupWindow = 16974450; // 0x1030272
- field public static final int Widget_Quantum_ListView = 16974451; // 0x1030273
- field public static final int Widget_Quantum_ListView_DropDown = 16974452; // 0x1030274
- field public static final int Widget_Quantum_MediaRouteButton = 16974453; // 0x1030275
- field public static final int Widget_Quantum_PopupMenu = 16974454; // 0x1030276
- field public static final int Widget_Quantum_PopupMenu_Overflow = 16974455; // 0x1030277
- field public static final int Widget_Quantum_PopupWindow = 16974456; // 0x1030278
- field public static final int Widget_Quantum_ProgressBar = 16974457; // 0x1030279
- field public static final int Widget_Quantum_ProgressBar_Horizontal = 16974458; // 0x103027a
- field public static final int Widget_Quantum_ProgressBar_Large = 16974459; // 0x103027b
- field public static final int Widget_Quantum_ProgressBar_Small = 16974460; // 0x103027c
- field public static final int Widget_Quantum_ProgressBar_Small_Title = 16974461; // 0x103027d
- field public static final int Widget_Quantum_RatingBar = 16974462; // 0x103027e
- field public static final int Widget_Quantum_RatingBar_Indicator = 16974463; // 0x103027f
- field public static final int Widget_Quantum_RatingBar_Small = 16974464; // 0x1030280
- field public static final int Widget_Quantum_ScrollView = 16974465; // 0x1030281
- field public static final int Widget_Quantum_SeekBar = 16974466; // 0x1030282
- field public static final int Widget_Quantum_SegmentedButton = 16974467; // 0x1030283
- field public static final int Widget_Quantum_Spinner = 16974469; // 0x1030285
- field public static final int Widget_Quantum_StackView = 16974468; // 0x1030284
- field public static final int Widget_Quantum_Tab = 16974470; // 0x1030286
- field public static final int Widget_Quantum_TabWidget = 16974471; // 0x1030287
- field public static final int Widget_Quantum_TextView = 16974472; // 0x1030288
- field public static final int Widget_Quantum_TextView_SpinnerItem = 16974473; // 0x1030289
- field public static final int Widget_Quantum_WebTextView = 16974474; // 0x103028a
- field public static final int Widget_Quantum_WebView = 16974475; // 0x103028b
field public static final int Widget_RatingBar = 16973857; // 0x1030021
field public static final int Widget_ScrollView = 16973869; // 0x103002d
field public static final int Widget_SeekBar = 16973856; // 0x1030020
@@ -2460,6 +2469,8 @@
field public static final int Widget_TextView = 16973858; // 0x1030022
field public static final int Widget_TextView_PopupMenu = 16973865; // 0x1030029
field public static final int Widget_TextView_SpinnerItem = 16973866; // 0x103002a
+ field public static final int Widget_Toolbar = 16974339; // 0x1030203
+ field public static final int Widget_Toolbar_Button_Navigation = 16974340; // 0x1030204
field public static final int Widget_WebView = 16973875; // 0x1030033
field public static final int l_resource_pad1 = 16974336; // 0x1030200
field public static final int l_resource_pad10 = 16974327; // 0x10301f7
@@ -3338,6 +3349,7 @@
method public void openContextMenu(android.view.View);
method public void openOptionsMenu();
method public void overridePendingTransition(int, int);
+ method public void postponeEnterTransition();
method public void recreate();
method public void registerForContextMenu(android.view.View);
method public final deprecated void removeDialog(int);
@@ -3393,6 +3405,7 @@
method public deprecated void startManagingCursor(android.database.Cursor);
method public boolean startNextMatchingActivity(android.content.Intent);
method public boolean startNextMatchingActivity(android.content.Intent, android.os.Bundle);
+ method public void startPostponedEnterTransition();
method public void startSearch(java.lang.String, boolean, android.os.Bundle, boolean);
method public void stopLockTask();
method public deprecated void stopManagingCursor(android.database.Cursor);
@@ -4487,6 +4500,7 @@
field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
+ field public static final java.lang.String INTENT_CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
field public static final int PRIORITY_DEFAULT = 0; // 0x0
field public static final int PRIORITY_HIGH = 1; // 0x1
field public static final int PRIORITY_LOW = -1; // 0xffffffff
@@ -4843,6 +4857,9 @@
field public static final int MENU_KEYCODE = 47; // 0x2f
field public static final java.lang.String QUERY = "query";
field public static final java.lang.String SHORTCUT_MIME_TYPE = "vnd.android.cursor.item/vnd.android.search.suggest";
+ field public static final java.lang.String SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG = "suggest_audio_channel_config";
+ field public static final java.lang.String SUGGEST_COLUMN_CONTENT_TYPE = "suggest_content_type";
+ field public static final java.lang.String SUGGEST_COLUMN_DURATION = "suggest_duration";
field public static final java.lang.String SUGGEST_COLUMN_FLAGS = "suggest_flags";
field public static final java.lang.String SUGGEST_COLUMN_FORMAT = "suggest_format";
field public static final java.lang.String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1";
@@ -4851,13 +4868,22 @@
field public static final java.lang.String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data";
field public static final java.lang.String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id";
field public static final java.lang.String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
+ field public static final java.lang.String SUGGEST_COLUMN_IS_LIVE = "suggest_is_live";
field public static final java.lang.String SUGGEST_COLUMN_LAST_ACCESS_HINT = "suggest_last_access_hint";
+ field public static final java.lang.String SUGGEST_COLUMN_PRODUCTION_YEAR = "suggest_production_year";
+ field public static final java.lang.String SUGGEST_COLUMN_PURCHASE_PRICE = "suggest_purchase_price";
field public static final java.lang.String SUGGEST_COLUMN_QUERY = "suggest_intent_query";
+ field public static final java.lang.String SUGGEST_COLUMN_RATING_SCORE = "suggest_rating_score";
+ field public static final java.lang.String SUGGEST_COLUMN_RATING_STYLE = "suggest_rating_style";
+ field public static final java.lang.String SUGGEST_COLUMN_RENTAL_PRICE = "suggest_rental_price";
+ field public static final java.lang.String SUGGEST_COLUMN_RESULT_CARD_IMAGE = "suggest_result_card_image";
field public static final java.lang.String SUGGEST_COLUMN_SHORTCUT_ID = "suggest_shortcut_id";
field public static final java.lang.String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = "suggest_spinner_while_refreshing";
field public static final java.lang.String SUGGEST_COLUMN_TEXT_1 = "suggest_text_1";
field public static final java.lang.String SUGGEST_COLUMN_TEXT_2 = "suggest_text_2";
field public static final java.lang.String SUGGEST_COLUMN_TEXT_2_URL = "suggest_text_2_url";
+ field public static final java.lang.String SUGGEST_COLUMN_VIDEO_HEIGHT = "suggest_video_height";
+ field public static final java.lang.String SUGGEST_COLUMN_VIDEO_WIDTH = "suggest_video_width";
field public static final java.lang.String SUGGEST_MIME_TYPE = "vnd.android.cursor.dir/vnd.android.search.suggest";
field public static final java.lang.String SUGGEST_NEVER_MAKE_SHORTCUT = "_-1";
field public static final java.lang.String SUGGEST_PARAMETER_LIMIT = "limit";
@@ -5171,11 +5197,11 @@
method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
method public void addUserRestriction(android.content.ComponentName, java.lang.String);
method public void clearCrossProfileIntentFilters(android.content.ComponentName);
+ method public void clearDeviceOwnerApp();
method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+ method public android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle);
method public android.os.UserHandle createUser(android.content.ComponentName, java.lang.String);
- method public 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);
@@ -5204,6 +5230,7 @@
method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String);
method public boolean isDeviceOwnerApp(java.lang.String);
method public boolean isLockTaskPermitted(android.content.ComponentName);
+ method public boolean isMasterVolumeMuted(android.content.ComponentName);
method public boolean isProfileOwnerApp(java.lang.String);
method public void lockNow();
method public void removeActiveAdmin(android.content.ComponentName);
@@ -5217,6 +5244,7 @@
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException;
+ method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
@@ -5230,12 +5258,14 @@
method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
method public void setPasswordQuality(android.content.ComponentName, int);
method public void setProfileEnabled(android.content.ComponentName);
+ method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public void wipeData(int);
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
field public static final java.lang.String ACTION_PROVISION_MANAGED_PROFILE = "android.app.action.ACTION_PROVISION_MANAGED_PROFILE";
+ field public static final java.lang.String ACTION_SEND_PROVISIONING_VALUES = "android.app.action.ACTION_SEND_PROVISIONING_VALUES";
field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
@@ -5244,8 +5274,10 @@
field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
- field public static final java.lang.String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = "defaultManagedProfileName";
- field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "deviceAdminPackageName";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME = "android.app.extra.defaultManagedProfileName";
+ field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.deviceAdminPackageName";
+ field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.ManagedProfileEmailAddress";
+ field public static final java.lang.String EXTRA_PROVISIONING_TOKEN = "android.app.extra.token";
field public static int FLAG_MANAGED_CAN_ACCESS_PARENT;
field public static int FLAG_PARENT_CAN_ACCESS_MANAGED;
field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
@@ -5350,26 +5382,12 @@
}
-package android.app.maintenance {
+package android.app.job {
- public abstract class IdleService extends android.app.Service {
- ctor public IdleService();
- method public final void finishIdle();
- method public final android.os.IBinder onBind(android.content.Intent);
- method public abstract boolean onIdleStart();
- method public abstract void onIdleStop();
- field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_IDLE_SERVICE";
- field public static final java.lang.String SERVICE_INTERFACE = "android.service.idle.IdleService";
- }
-
-}
-
-package android.app.task {
-
- public class Task implements android.os.Parcelable {
+ public class JobInfo implements android.os.Parcelable {
method public int describeContents();
method public int getBackoffPolicy();
- method public android.os.Bundle getExtras();
+ method public android.os.PersistableBundle getExtras();
method public int getId();
method public long getInitialBackoffMillis();
method public long getIntervalMillis();
@@ -5384,55 +5402,55 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
- public static abstract interface Task.BackoffPolicy {
+ public static abstract interface JobInfo.BackoffPolicy {
field public static final int EXPONENTIAL = 1; // 0x1
field public static final int LINEAR = 0; // 0x0
}
- public static final class Task.Builder {
- ctor public Task.Builder(int, android.content.ComponentName);
- method public android.app.task.Task build();
- method public android.app.task.Task.Builder setBackoffCriteria(long, int);
- method public android.app.task.Task.Builder setExtras(android.os.Bundle);
- method public android.app.task.Task.Builder setMinimumLatency(long);
- method public android.app.task.Task.Builder setOverrideDeadline(long);
- method public android.app.task.Task.Builder setPeriodic(long);
- method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int);
- method public android.app.task.Task.Builder setRequiresCharging(boolean);
- method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean);
+ public static final class JobInfo.Builder {
+ ctor public JobInfo.Builder(int, android.content.ComponentName);
+ method public android.app.job.JobInfo build();
+ method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
+ method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle);
+ method public android.app.job.JobInfo.Builder setMinimumLatency(long);
+ method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
+ method public android.app.job.JobInfo.Builder setPeriodic(long);
+ method public android.app.job.JobInfo.Builder setRequiredNetworkCapabilities(int);
+ method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
+ method public android.app.job.JobInfo.Builder setRequiresDeviceIdle(boolean);
}
- public static abstract interface Task.NetworkType {
+ public static abstract interface JobInfo.NetworkType {
field public static final int ANY = 1; // 0x1
field public static final int NONE = 0; // 0x0
field public static final int UNMETERED = 2; // 0x2
}
- public abstract class TaskManager {
- ctor public TaskManager();
- method public abstract void cancel(int);
- method public abstract void cancelAll();
- method public abstract java.util.List<android.app.task.Task> getAllPendingTasks();
- method public abstract int schedule(android.app.task.Task);
- field public static final int RESULT_FAILURE = 0; // 0x0
- field public static final int RESULT_SUCCESS = 1; // 0x1
- }
-
- public class TaskParams implements android.os.Parcelable {
+ public class JobParameters implements android.os.Parcelable {
method public int describeContents();
- method public android.os.Bundle getExtras();
- method public int getTaskId();
+ method public android.os.PersistableBundle getExtras();
+ method public int getJobId();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
- public abstract class TaskService extends android.app.Service {
- ctor public TaskService();
+ public abstract class JobScheduler {
+ ctor public JobScheduler();
+ method public abstract void cancel(int);
+ method public abstract void cancelAll();
+ method public abstract java.util.List<android.app.job.JobInfo> getAllPendingJobs();
+ method public abstract int schedule(android.app.job.JobInfo);
+ field public static final int RESULT_FAILURE = 0; // 0x0
+ field public static final int RESULT_SUCCESS = 1; // 0x1
+ }
+
+ public abstract class JobService extends android.app.Service {
+ ctor public JobService();
+ method public final void jobFinished(android.app.job.JobParameters, boolean);
method public final android.os.IBinder onBind(android.content.Intent);
- method public abstract boolean onStartTask(android.app.task.TaskParams);
- method public abstract boolean onStopTask(android.app.task.TaskParams);
- method public final void taskFinished(android.app.task.TaskParams, boolean);
- field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_TASK_SERVICE";
+ method public abstract boolean onStartJob(android.app.job.JobParameters);
+ method public abstract boolean onStopJob(android.app.job.JobParameters);
+ field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE";
}
}
@@ -6758,7 +6776,6 @@
method public final android.os.Bundle call(android.net.Uri, java.lang.String, java.lang.String, android.os.Bundle);
method public deprecated void cancelSync(android.net.Uri);
method public static void cancelSync(android.accounts.Account, java.lang.String);
- method public static void cancelSync(android.content.ComponentName);
method public static void cancelSync(android.content.SyncRequest);
method public final android.net.Uri canonicalize(android.net.Uri);
method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -6768,18 +6785,14 @@
method public static boolean getMasterSyncAutomatically();
method public java.util.List<android.content.UriPermission> getOutgoingPersistedUriPermissions();
method public static java.util.List<android.content.PeriodicSync> getPeriodicSyncs(android.accounts.Account, java.lang.String);
- method public static java.util.List<android.content.PeriodicSync> getPeriodicSyncs(android.content.ComponentName);
method public java.util.List<android.content.UriPermission> getPersistedUriPermissions();
method public java.lang.String[] getStreamTypes(android.net.Uri, java.lang.String);
method public static android.content.SyncAdapterType[] getSyncAdapterTypes();
method public static boolean getSyncAutomatically(android.accounts.Account, java.lang.String);
method public final java.lang.String getType(android.net.Uri);
method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
- method public static boolean isServiceActive(android.content.ComponentName);
method public static boolean isSyncActive(android.accounts.Account, java.lang.String);
- method public static boolean isSyncActive(android.content.ComponentName);
method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
- method public static boolean isSyncPending(android.content.ComponentName);
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -6801,7 +6814,6 @@
method public static void requestSync(android.content.SyncRequest);
method public static void setIsSyncable(android.accounts.Account, java.lang.String, int);
method public static void setMasterSyncAutomatically(boolean);
- method public static void setServiceActive(android.content.ComponentName, boolean);
method public static void setSyncAutomatically(android.accounts.Account, java.lang.String, boolean);
method public deprecated void startSync(android.net.Uri, android.os.Bundle);
method public void takePersistableUriPermission(android.net.Uri, int);
@@ -7005,10 +7017,9 @@
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 HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
+ field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater";
@@ -7030,7 +7041,6 @@
field public static final java.lang.String SEARCH_SERVICE = "search";
field public static final java.lang.String SENSOR_SERVICE = "sensor";
field public static final java.lang.String STORAGE_SERVICE = "storage";
- field public static final java.lang.String TASK_SERVICE = "task";
field public static final java.lang.String TELEPHONY_SERVICE = "phone";
field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
@@ -7401,6 +7411,7 @@
field public static final java.lang.String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED";
field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
+ field public static final java.lang.String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
field public static final java.lang.String ACTION_PACKAGE_ADDED = "android.intent.action.PACKAGE_ADDED";
field public static final java.lang.String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED";
field public static final java.lang.String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED";
@@ -7415,7 +7426,6 @@
field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE";
field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK";
field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
- field public static final java.lang.String ACTION_PICK_DIRECTORY = "android.intent.action.PICK_DIRECTORY";
field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
field public static final java.lang.String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
field public static final java.lang.String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY";
@@ -7475,7 +7485,6 @@
field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
field public static final java.lang.String CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK";
field public static final java.lang.String CATEGORY_MONKEY = "android.intent.category.MONKEY";
- field public static final java.lang.String CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
field public static final java.lang.String CATEGORY_PREFERENCE = "android.intent.category.PREFERENCE";
field public static final java.lang.String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
@@ -7547,12 +7556,12 @@
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
- field public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288; // 0x80000
+ field public static final deprecated int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 524288; // 0x80000
field public static final int FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS = 8388608; // 0x800000
field public static final int FLAG_ACTIVITY_FORWARD_RESULT = 33554432; // 0x2000000
field public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 1048576; // 0x100000
field public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 134217728; // 0x8000000
- field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 268959744; // 0x10080000
+ field public static final int FLAG_ACTIVITY_NEW_DOCUMENT = 524288; // 0x80000
field public static final int FLAG_ACTIVITY_NEW_TASK = 268435456; // 0x10000000
field public static final int FLAG_ACTIVITY_NO_ANIMATION = 65536; // 0x10000
field public static final int FLAG_ACTIVITY_NO_HISTORY = 1073741824; // 0x40000000
@@ -7770,9 +7779,7 @@
field public final android.accounts.Account account;
field public final java.lang.String authority;
field public final android.os.Bundle extras;
- field public final boolean isService;
field public final long period;
- field public final android.content.ComponentName service;
}
public class ReceiverCallNotAllowedException extends android.util.AndroidRuntimeException {
@@ -7917,13 +7924,11 @@
method public void writeToParcel(android.os.Parcel, int);
field public final android.accounts.Account account;
field public final java.lang.String authority;
- field public final android.content.ComponentName service;
field public final long startTime;
}
public class SyncRequest implements android.os.Parcelable {
method public int describeContents();
- method public boolean isExpedited();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -7938,10 +7943,7 @@
method public android.content.SyncRequest.Builder setIgnoreSettings(boolean);
method public android.content.SyncRequest.Builder setManual(boolean);
method public android.content.SyncRequest.Builder setNoRetry(boolean);
- method public android.content.SyncRequest.Builder setPriority(int);
method public android.content.SyncRequest.Builder setSyncAdapter(android.accounts.Account, java.lang.String);
- method public android.content.SyncRequest.Builder setSyncAdapter(android.content.ComponentName);
- method public android.content.SyncRequest.Builder setTransferSize(long, long);
method public android.content.SyncRequest.Builder syncOnce();
method public android.content.SyncRequest.Builder syncPeriodic(long, long);
}
@@ -7969,13 +7971,6 @@
field public boolean tooManyRetries;
}
- public abstract class SyncService extends android.app.Service {
- ctor public SyncService();
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract void onPerformSync(android.os.Bundle, android.content.SyncResult);
- method protected boolean parallelSyncsEnabled();
- }
-
public class SyncStats implements android.os.Parcelable {
ctor public SyncStats();
ctor public SyncStats(android.os.Parcel);
@@ -8044,6 +8039,7 @@
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_NEVER = 3; // 0x3
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
@@ -8084,6 +8080,7 @@
field public int documentLaunchMode;
field public int flags;
field public int launchMode;
+ field public int maxRecents;
field public java.lang.String parentActivityName;
field public java.lang.String permission;
field public int screenOrientation;
@@ -8250,11 +8247,11 @@
}
public static abstract interface LauncherApps.OnAppsChangedListener {
- method public abstract void onPackageAdded(java.lang.String, android.os.UserHandle);
- method public abstract void onPackageChanged(java.lang.String, android.os.UserHandle);
- method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
- method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
- method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
+ method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String);
+ method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String);
+ method public abstract void onPackageRemoved(android.os.UserHandle, java.lang.String);
+ method public abstract void onPackagesAvailable(android.os.UserHandle, java.lang.String[], boolean);
+ method public abstract void onPackagesUnavailable(android.os.UserHandle, java.lang.String[], boolean);
}
public class PackageInfo implements android.os.Parcelable {
@@ -8422,7 +8419,7 @@
field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location";
field public static final java.lang.String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network";
- field public static final java.lang.String FEATURE_MANAGEDPROFILES = "android.software.managedprofiles";
+ field public static final java.lang.String FEATURE_MANAGED_PROFILES = "android.software.managed_profiles";
field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
@@ -10128,13 +10125,13 @@
field public android.graphics.Bitmap inBitmap;
field public int inDensity;
field public boolean inDither;
- field public boolean inInputShareable;
+ field public deprecated boolean inInputShareable;
field public boolean inJustDecodeBounds;
field public boolean inMutable;
field public boolean inPreferQualityOverSpeed;
field public android.graphics.Bitmap.Config inPreferredConfig;
field public boolean inPremultiplied;
- field public boolean inPurgeable;
+ field public deprecated boolean inPurgeable;
field public int inSampleSize;
field public boolean inScaled;
field public int inScreenDensity;
@@ -11271,14 +11268,11 @@
method public static android.graphics.drawable.Drawable createFromPath(java.lang.String);
method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String);
method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options);
- method public static android.graphics.drawable.Drawable createFromResourceStreamThemed(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.content.res.Resources.Theme);
- method public static android.graphics.drawable.Drawable createFromResourceStreamThemed(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, java.lang.String, android.graphics.BitmapFactory.Options, android.content.res.Resources.Theme);
method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, java.lang.String);
- method public static android.graphics.drawable.Drawable createFromStreamThemed(java.io.InputStream, java.lang.String, android.content.res.Resources.Theme);
method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static android.graphics.drawable.Drawable createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public static android.graphics.drawable.Drawable createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public static android.graphics.drawable.Drawable createFromXmlInnerThemed(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
- method public static android.graphics.drawable.Drawable createFromXmlThemed(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static android.graphics.drawable.Drawable createFromXmlInner(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public abstract void draw(android.graphics.Canvas);
method public int getAlpha();
method public final android.graphics.Rect getBounds();
@@ -11507,6 +11501,7 @@
public class RippleDrawable extends android.graphics.drawable.LayerDrawable {
ctor public RippleDrawable(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
+ method public void setColor(android.content.res.ColorStateList);
}
public class RotateDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
@@ -11554,7 +11549,6 @@
method public android.graphics.Paint getPaint();
method public android.graphics.drawable.ShapeDrawable.ShaderFactory getShaderFactory();
method public android.graphics.drawable.shapes.Shape getShape();
- method public android.content.res.ColorStateList getTint();
method protected boolean inflateTag(java.lang.String, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet);
method protected void onDraw(android.graphics.drawable.shapes.Shape, android.graphics.Canvas, android.graphics.Paint);
method public void setAlpha(int);
@@ -11565,7 +11559,6 @@
method public void setPadding(android.graphics.Rect);
method public void setShaderFactory(android.graphics.drawable.ShapeDrawable.ShaderFactory);
method public void setShape(android.graphics.drawable.shapes.Shape);
- method public void setTint(android.content.res.ColorStateList);
}
public static abstract class ShapeDrawable.ShaderFactory {
@@ -11972,14 +11965,18 @@
method public int getMinDelay();
method public java.lang.String getName();
method public float getPower();
+ method public int getReportingMode();
method public float getResolution();
method public java.lang.String getStringType();
method public int getType();
method public java.lang.String getVendor();
method public int getVersion();
method public boolean isWakeUpSensor();
+ field public static final int REPORTING_MODE_CONTINUOUS = 0; // 0x0
+ field public static final int REPORTING_MODE_ONE_SHOT = 2; // 0x2
+ field public static final int REPORTING_MODE_ON_CHANGE = 1; // 0x1
+ field public static final int REPORTING_MODE_SPECIAL_TRIGGER = 3; // 0x3
field public static final java.lang.String SENSOR_STRING_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR = "android.sensor.non_wake_up_proximity_sensor";
- field public static final java.lang.String SENSOR_STRING_TYPE_WAKE_UP_TILT_DETECTOR = "android.sensor.wake_up_tilt_detector";
field public static final java.lang.String STRING_TYPE_ACCELEROMETER = "android.sensor.accelerometer";
field public static final java.lang.String STRING_TYPE_AMBIENT_TEMPERATURE = "android.sensor.ambient_temperature";
field public static final java.lang.String STRING_TYPE_GAME_ROTATION_VECTOR = "android.sensor.game_rotation_vector";
@@ -12060,7 +12057,6 @@
field public static final int TYPE_WAKE_UP_ROTATION_VECTOR = 31; // 0x1f
field public static final int TYPE_WAKE_UP_STEP_COUNTER = 38; // 0x26
field public static final int TYPE_WAKE_UP_STEP_DETECTOR = 37; // 0x25
- field public static final int TYPE_WAKE_UP_TILT_DETECTOR = 41; // 0x29
}
public class SensorEvent {
@@ -12209,12 +12205,12 @@
public static abstract class CameraCaptureSession.CaptureListener {
ctor public CameraCaptureSession.CaptureListener();
- method public void onCaptureCompleted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
- method public void onCaptureFailed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
- method public void onCaptureProgressed(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
- method public void onCaptureSequenceAborted(android.hardware.camera2.CameraDevice, int);
- method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraDevice, int, long);
- method public void onCaptureStarted(android.hardware.camera2.CameraDevice, android.hardware.camera2.CaptureRequest, long);
+ method public void onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult);
+ method public void onCaptureFailed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureFailure);
+ method public void onCaptureProgressed(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.CaptureResult);
+ method public void onCaptureSequenceAborted(android.hardware.camera2.CameraCaptureSession, int);
+ method public void onCaptureSequenceCompleted(android.hardware.camera2.CameraCaptureSession, int, long);
+ method public void onCaptureStarted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, long);
}
public static abstract class CameraCaptureSession.StateListener {
@@ -12258,7 +12254,6 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key LENS_INFO_MINIMUM_FOCUS_DISTANCE;
field public static final android.hardware.camera2.CameraCharacteristics.Key NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_AVAILABLE_CAPABILITIES;
- field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_INPUT_STREAMS;
field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_PROC;
field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
field public static final android.hardware.camera2.CameraCharacteristics.Key REQUEST_MAX_NUM_OUTPUT_RAW;
@@ -12301,7 +12296,7 @@
method public final int hashCode();
}
- public abstract interface CameraDevice implements java.lang.AutoCloseable {
+ public abstract class CameraDevice implements java.lang.AutoCloseable {
method public abstract deprecated int capture(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract deprecated int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraDevice.CaptureListener, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void close();
@@ -12483,7 +12478,6 @@
field public static final int REQUEST_AVAILABLE_CAPABILITIES_DNG = 5; // 0x5
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 3; // 0x3
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 2; // 0x2
- field public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4; // 0x4
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -12694,14 +12688,7 @@
public final class DngCreator implements java.lang.AutoCloseable {
ctor public DngCreator(android.hardware.camera2.CameraCharacteristics, android.hardware.camera2.CaptureResult);
method public void close();
- method public android.hardware.camera2.DngCreator setDescription(java.lang.String);
- method public android.hardware.camera2.DngCreator setLocation(android.location.Location);
- method public android.hardware.camera2.DngCreator setOrientation(int);
- method public android.hardware.camera2.DngCreator setThumbnail(android.graphics.Bitmap);
- method public android.hardware.camera2.DngCreator setThumbnail(android.media.Image);
- method public void writeByteBuffer(java.io.OutputStream, android.util.Size, java.nio.ByteBuffer, long) throws java.io.IOException;
method public void writeImage(java.io.OutputStream, android.media.Image) throws java.io.IOException;
- method public void writeInputStream(java.io.OutputStream, android.util.Size, java.io.InputStream, long) throws java.io.IOException;
}
public final class TotalCaptureResult extends android.hardware.camera2.CaptureResult {
@@ -12789,6 +12776,7 @@
}
public final class TonemapCurve {
+ ctor public TonemapCurve(float[], float[], float[]);
method public void copyColorCurve(int, float[], int);
method public android.graphics.PointF getPoint(int, int);
method public int getPointCount(int);
@@ -12833,199 +12821,6 @@
}
-package android.hardware.hdmi {
-
- public final class HdmiCec {
- method public static java.lang.String getDefaultDeviceName(int);
- method public static int getTypeFromAddress(int);
- method public static boolean isValidAddress(int);
- method public static boolean isValidType(int);
- field public static final int ADDR_AUDIO_SYSTEM = 5; // 0x5
- field public static final int ADDR_BROADCAST = 15; // 0xf
- field public static final int ADDR_INVALID = -1; // 0xffffffff
- field public static final int ADDR_PLAYBACK_1 = 4; // 0x4
- field public static final int ADDR_PLAYBACK_2 = 8; // 0x8
- field public static final int ADDR_PLAYBACK_3 = 11; // 0xb
- field public static final int ADDR_RECORDER_1 = 1; // 0x1
- field public static final int ADDR_RECORDER_2 = 2; // 0x2
- field public static final int ADDR_RECORDER_3 = 9; // 0x9
- field public static final int ADDR_RESERVED_1 = 12; // 0xc
- field public static final int ADDR_RESERVED_2 = 13; // 0xd
- field public static final int ADDR_SPECIFIC_USE = 14; // 0xe
- field public static final int ADDR_TUNER_1 = 3; // 0x3
- field public static final int ADDR_TUNER_2 = 6; // 0x6
- field public static final int ADDR_TUNER_3 = 7; // 0x7
- field public static final int ADDR_TUNER_4 = 10; // 0xa
- field public static final int ADDR_TV = 0; // 0x0
- field public static final int ADDR_UNREGISTERED = 15; // 0xf
- field public static final int DEVICE_AUDIO_SYSTEM = 5; // 0x5
- field public static final int DEVICE_INACTIVE = -1; // 0xffffffff
- field public static final int DEVICE_PLAYBACK = 4; // 0x4
- field public static final int DEVICE_RECORDER = 1; // 0x1
- field public static final int DEVICE_RESERVED = 2; // 0x2
- field public static final int DEVICE_TUNER = 3; // 0x3
- field public static final int DEVICE_TV = 0; // 0x0
- field public static final int MESSAGE_ABORT = 255; // 0xff
- field public static final int MESSAGE_ACTIVE_SOURCE = 130; // 0x82
- field public static final int MESSAGE_CEC_VERSION = 158; // 0x9e
- field public static final int MESSAGE_CLEAR_ANALOG_TIMER = 51; // 0x33
- field public static final int MESSAGE_CLEAR_DIGITAL_TIMER = 153; // 0x99
- field public static final int MESSAGE_CLEAR_EXTERNAL_TIMER = 161; // 0xa1
- field public static final int MESSAGE_DECK_CONTROL = 66; // 0x42
- field public static final int MESSAGE_DECK_STATUS = 27; // 0x1b
- field public static final int MESSAGE_DEVICE_VENDOR_ID = 135; // 0x87
- field public static final int MESSAGE_FEATURE_ABORT = 0; // 0x0
- field public static final int MESSAGE_GET_CEC_VERSION = 159; // 0x9f
- field public static final int MESSAGE_GET_MENU_LANGUAGE = 145; // 0x91
- field public static final int MESSAGE_GIVE_AUDIO_STATUS = 113; // 0x71
- field public static final int MESSAGE_GIVE_DECK_STATUS = 26; // 0x1a
- field public static final int MESSAGE_GIVE_DEVICE_POWER_STATUS = 143; // 0x8f
- field public static final int MESSAGE_GIVE_DEVICE_VENDOR_ID = 140; // 0x8c
- field public static final int MESSAGE_GIVE_OSD_NAME = 70; // 0x46
- field public static final int MESSAGE_GIVE_PHYSICAL_ADDRESS = 131; // 0x83
- field public static final int MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 125; // 0x7d
- field public static final int MESSAGE_GIVE_TUNER_DEVICE_STATUS = 8; // 0x8
- field public static final int MESSAGE_IMAGE_VIEW_ON = 4; // 0x4
- field public static final int MESSAGE_INACTIVE_SOURCE = 157; // 0x9d
- field public static final int MESSAGE_INITIATE_ARC = 192; // 0xc0
- field public static final int MESSAGE_MENU_REQUEST = 141; // 0x8d
- field public static final int MESSAGE_MENU_STATUS = 142; // 0x8e
- field public static final int MESSAGE_PLAY = 65; // 0x41
- field public static final int MESSAGE_RECORD_OFF = 11; // 0xb
- field public static final int MESSAGE_RECORD_ON = 9; // 0x9
- field public static final int MESSAGE_RECORD_STATUS = 10; // 0xa
- field public static final int MESSAGE_RECORD_TV_SCREEN = 15; // 0xf
- field public static final int MESSAGE_REPORT_ARC_INITIATED = 193; // 0xc1
- field public static final int MESSAGE_REPORT_ARC_TERMINATED = 194; // 0xc2
- field public static final int MESSAGE_REPORT_AUDIO_STATUS = 122; // 0x7a
- field public static final int MESSAGE_REPORT_PHYSICAL_ADDRESS = 132; // 0x84
- field public static final int MESSAGE_REPORT_POWER_STATUS = 144; // 0x90
- field public static final int MESSAGE_REQUEST_ACTIVE_SOURCE = 133; // 0x85
- field public static final int MESSAGE_REQUEST_ARC_INITIATION = 195; // 0xc3
- field public static final int MESSAGE_REQUEST_ARC_TERMINATION = 196; // 0xc4
- field public static final int MESSAGE_ROUTING_CHANGE = 128; // 0x80
- field public static final int MESSAGE_ROUTING_INFORMATION = 129; // 0x81
- field public static final int MESSAGE_SELECT_ANALOG_SERVICE = 146; // 0x92
- field public static final int MESSAGE_SELECT_DIGITAL_SERVICE = 147; // 0x93
- field public static final int MESSAGE_SET_ANALOG_TIMER = 52; // 0x34
- field public static final int MESSAGE_SET_AUDIO_RATE = 154; // 0x9a
- field public static final int MESSAGE_SET_DIGITAL_TIMER = 151; // 0x97
- field public static final int MESSAGE_SET_EXTERNAL_TIMER = 162; // 0xa2
- field public static final int MESSAGE_SET_MENU_LANGUAGE = 50; // 0x32
- field public static final int MESSAGE_SET_OSD_NAME = 71; // 0x47
- field public static final int MESSAGE_SET_OSD_STRING = 100; // 0x64
- field public static final int MESSAGE_SET_STREAM_PATH = 134; // 0x86
- field public static final int MESSAGE_SET_SYSTEM_AUDIO_MODE = 114; // 0x72
- field public static final int MESSAGE_SET_TIMER_PROGRAM_TITLE = 103; // 0x67
- field public static final int MESSAGE_STANDBY = 54; // 0x36
- field public static final int MESSAGE_SYSTEM_AUDIO_MODE_REQUEST = 112; // 0x70
- field public static final int MESSAGE_SYSTEM_AUDIO_MODE_STATUS = 126; // 0x7e
- field public static final int MESSAGE_TERMINATE_ARC = 197; // 0xc5
- field public static final int MESSAGE_TEXT_VIEW_ON = 13; // 0xd
- field public static final int MESSAGE_TIMER_CLEARED_STATUS = 67; // 0x43
- field public static final int MESSAGE_TIMER_STATUS = 53; // 0x35
- field public static final int MESSAGE_TUNER_DEVICE_STATUS = 7; // 0x7
- field public static final int MESSAGE_TUNER_STEP_DECREMENT = 6; // 0x6
- field public static final int MESSAGE_TUNER_STEP_INCREMENT = 5; // 0x5
- field public static final int MESSAGE_USER_CONTROL_PRESSED = 68; // 0x44
- field public static final int MESSAGE_USER_CONTROL_RELEASED = 69; // 0x45
- field public static final int MESSAGE_VENDOR_COMMAND = 137; // 0x89
- field public static final int MESSAGE_VENDOR_COMMAND_WITH_ID = 160; // 0xa0
- field public static final int MESSAGE_VENDOR_REMOTE_BUTTON_DOWN = 138; // 0x8a
- field public static final int MESSAGE_VENDOR_REMOTE_BUTTON_UP = 139; // 0x8b
- field public static final int POWER_STATUS_ON = 0; // 0x0
- field public static final int POWER_STATUS_STANDBY = 1; // 0x1
- field public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; // 0x2
- field public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; // 0x3
- field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff
- field public static final int RESULT_ALREADY_IN_PROGRESS = 4; // 0x4
- field public static final int RESULT_EXCEPTION = 5; // 0x5
- field public static final int RESULT_SOURCE_NOT_AVAILABLE = 2; // 0x2
- field public static final int RESULT_SUCCESS = 0; // 0x0
- field public static final int RESULT_TARGET_NOT_AVAILABLE = 3; // 0x3
- field public static final int RESULT_TIMEOUT = 1; // 0x1
- field public static final int UNKNOWN_VENDOR_ID = 16777215; // 0xffffff
- }
-
- public final class HdmiCecClient {
- method public boolean isTvOn();
- method public void sendActiveSource();
- method public void sendGiveDevicePowerStatus(int);
- method public void sendImageViewOn();
- method public void sendInactiveSource();
- method public void sendTextViewOn();
- }
-
- public static abstract class HdmiCecClient.Listener {
- ctor public HdmiCecClient.Listener();
- method public void onCableStatusChanged(boolean);
- method public void onMessageReceived(android.hardware.hdmi.HdmiCecMessage);
- }
-
- public final class HdmiCecDeviceInfo implements android.os.Parcelable {
- method public int describeContents();
- method public int getDeviceType();
- method public java.lang.String getDisplayName();
- method public int getLogicalAddress();
- method public int getPhysicalAddress();
- method public int getVendorId();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
- public final class HdmiCecManager {
- method public android.hardware.hdmi.HdmiCecClient getClient(int, android.hardware.hdmi.HdmiCecClient.Listener);
- }
-
- public final class HdmiCecMessage implements android.os.Parcelable {
- ctor public HdmiCecMessage(int, int, int, byte[]);
- method public int describeContents();
- method public int getDestination();
- method public int getOpcode();
- method public byte[] getParams();
- method public int getSource();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- field public static final byte[] EMPTY_PARAM;
- }
-
- public final class HdmiControlManager {
- method public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
- method public android.hardware.hdmi.HdmiPlaybackClient getPlaybackClient();
- method public android.hardware.hdmi.HdmiTvClient getTvClient();
- method public void removeHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
- }
-
- public static abstract interface HdmiControlManager.HotplugEventListener {
- method public abstract void onReceived(android.hardware.hdmi.HdmiHotplugEvent);
- }
-
- public final class HdmiHotplugEvent implements android.os.Parcelable {
- method public int describeContents();
- method public int getPort();
- method public boolean isConnected();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
- public final class HdmiPlaybackClient {
- method public void oneTouchPlay(android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback);
- method public void queryDisplayStatus(android.hardware.hdmi.HdmiPlaybackClient.DisplayStatusCallback);
- }
-
- public static abstract interface HdmiPlaybackClient.DisplayStatusCallback {
- method public abstract void onComplete(int);
- }
-
- public static abstract interface HdmiPlaybackClient.OneTouchPlayCallback {
- method public abstract void onComplete(int);
- }
-
- public final class HdmiTvClient {
- }
-
-}
-
package android.hardware.input {
public final class InputManager {
@@ -13807,11 +13602,12 @@
method public void stop();
}
- public final class AudioAttributes {
+ public final class AudioAttributes implements android.os.Parcelable {
+ method public int describeContents();
method public int getContentType();
method public int getFlags();
- method public java.util.Set<java.lang.String> getTags();
method public int getUsage();
+ method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
@@ -13838,7 +13634,6 @@
public static class AudioAttributes.Builder {
ctor public AudioAttributes.Builder();
ctor public AudioAttributes.Builder(android.media.AudioAttributes);
- method public android.media.AudioAttributes.Builder addTag(java.lang.String);
method public android.media.AudioAttributes build();
method public android.media.AudioAttributes.Builder setContentType(int);
method public android.media.AudioAttributes.Builder setFlags(int);
@@ -13897,6 +13692,7 @@
method public void adjustStreamVolume(int, int, int);
method public void adjustSuggestedStreamVolume(int, int, int);
method public void adjustVolume(int, int);
+ method public int allocateAudioSessionId();
method public void dispatchMediaKeyEvent(android.view.KeyEvent);
method public int getMode();
method public java.lang.String getParameters(java.lang.String);
@@ -13957,6 +13753,7 @@
field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd
field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
+ field public static final int ERROR = -1; // 0xffffffff
field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE";
field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE";
@@ -14136,6 +13933,7 @@
method public static boolean hasProfile(int);
method public static boolean hasProfile(int, int);
field public static final int QUALITY_1080P = 6; // 0x6
+ field public static final int QUALITY_2160P = 8; // 0x8
field public static final int QUALITY_480P = 4; // 0x4
field public static final int QUALITY_720P = 5; // 0x5
field public static final int QUALITY_CIF = 3; // 0x3
@@ -14144,6 +13942,7 @@
field public static final int QUALITY_QCIF = 2; // 0x2
field public static final int QUALITY_QVGA = 7; // 0x7
field public static final int QUALITY_TIME_LAPSE_1080P = 1006; // 0x3ee
+ field public static final int QUALITY_TIME_LAPSE_2160P = 1008; // 0x3f0
field public static final int QUALITY_TIME_LAPSE_480P = 1004; // 0x3ec
field public static final int QUALITY_TIME_LAPSE_720P = 1005; // 0x3ed
field public static final int QUALITY_TIME_LAPSE_CIF = 1003; // 0x3eb
@@ -15978,6 +15777,189 @@
}
+package android.media.tv {
+
+ public final class TvContract {
+ method public static final android.net.Uri buildChannelLogoUri(long);
+ method public static final android.net.Uri buildChannelLogoUri(android.net.Uri);
+ method public static final android.net.Uri buildChannelUri(long);
+ method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName);
+ method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
+ method public static final android.net.Uri buildProgramUri(long);
+ method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
+ method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
+ field public static final java.lang.String AUTHORITY = "android.media.tv";
+ }
+
+ public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns {
+ field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
+ }
+
+ public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
+ field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
+ field public static final java.lang.String COLUMN_DESCRIPTION = "description";
+ field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
+ field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+ field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+ field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
+ field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
+ field public static final java.lang.String COLUMN_SERVICE_NAME = "service_name";
+ field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
+ field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
+ field public static final java.lang.String COLUMN_TYPE = "type";
+ field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+ field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final int SERVICE_TYPE_AUDIO = 2; // 0x2
+ field public static final int SERVICE_TYPE_AUDIO_VIDEO = 1; // 0x1
+ field public static final int SERVICE_TYPE_OTHER = 0; // 0x0
+ field public static final int TYPE_1SEG = 263168; // 0x40400
+ field public static final int TYPE_ATSC_C = 197120; // 0x30200
+ field public static final int TYPE_ATSC_M_H = 197376; // 0x30300
+ field public static final int TYPE_ATSC_T = 196608; // 0x30000
+ field public static final int TYPE_CMMB = 327936; // 0x50100
+ field public static final int TYPE_DTMB = 327680; // 0x50000
+ field public static final int TYPE_DVB_C = 131584; // 0x20200
+ field public static final int TYPE_DVB_C2 = 131585; // 0x20201
+ field public static final int TYPE_DVB_H = 131840; // 0x20300
+ field public static final int TYPE_DVB_S = 131328; // 0x20100
+ field public static final int TYPE_DVB_S2 = 131329; // 0x20101
+ field public static final int TYPE_DVB_SH = 132096; // 0x20400
+ field public static final int TYPE_DVB_T = 131072; // 0x20000
+ field public static final int TYPE_DVB_T2 = 131073; // 0x20001
+ field public static final int TYPE_ISDB_C = 262912; // 0x40300
+ field public static final int TYPE_ISDB_S = 262656; // 0x40200
+ field public static final int TYPE_ISDB_T = 262144; // 0x40000
+ field public static final int TYPE_ISDB_TB = 262400; // 0x40100
+ field public static final int TYPE_NTSC = 1; // 0x1
+ field public static final int TYPE_OTHER = 0; // 0x0
+ field public static final int TYPE_PAL = 2; // 0x2
+ field public static final int TYPE_PASSTHROUGH = 65536; // 0x10000
+ field public static final int TYPE_SECAM = 3; // 0x3
+ field public static final int TYPE_S_DMB = 393472; // 0x60100
+ field public static final int TYPE_T_DMB = 393216; // 0x60000
+ }
+
+ public static final class TvContract.Channels.Logo {
+ field public static final java.lang.String CONTENT_DIRECTORY = "logo";
+ }
+
+ public static final class TvContract.Programs implements android.media.tv.TvContract.BaseTvColumns {
+ field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
+ field public static final java.lang.String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+ field public static final java.lang.String COLUMN_CANONICAL_GENRE = "canonical_genre";
+ field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
+ field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+ field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+ field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
+ field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
+ field public static final java.lang.String COLUMN_SHORT_DESCRIPTION = "short_description";
+ field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+ field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+ field public static final java.lang.String COLUMN_TITLE = "title";
+ field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+ field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
+ field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
+ field public static final android.net.Uri CONTENT_URI;
+ }
+
+ public static final class TvContract.Programs.Genres {
+ method public static java.lang.String[] decode(java.lang.String);
+ method public static java.lang.String encode(java.lang.String...);
+ field public static final java.lang.String ANIMAL_WILDLIFE = "Animal/Wildlife";
+ field public static final java.lang.String COMEDY = "Comedy";
+ field public static final java.lang.String DRAMA = "Drama";
+ field public static final java.lang.String EDUCATION = "Education";
+ field public static final java.lang.String FAMILY_KIDS = "Family/Kids";
+ field public static final java.lang.String GAMING = "Gaming";
+ field public static final java.lang.String MOVIES = "Movies";
+ field public static final java.lang.String NEWS = "News";
+ field public static final java.lang.String SHOPPING = "Shopping";
+ field public static final java.lang.String SPORTS = "Sports";
+ field public static final java.lang.String TRAVEL = "Travel";
+ }
+
+ public final class TvInputInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getId();
+ method public android.content.Intent getIntentForSettingsActivity();
+ method public android.content.Intent getIntentForSetupActivity();
+ method public android.content.pm.ServiceInfo getServiceInfo();
+ method public int getType();
+ method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
+ method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String EXTRA_SERVICE_NAME = "serviceName";
+ field public static final int TYPE_HDMI = 1; // 0x1
+ field public static final int TYPE_PASSTHROUGH = 3; // 0x3
+ field public static final int TYPE_TUNER = 2; // 0x2
+ field public static final int TYPE_VIRTUAL = 0; // 0x0
+ }
+
+ public final class TvInputManager {
+ method public boolean getAvailability(java.lang.String);
+ method public java.util.List<android.media.tv.TvInputInfo> getTvInputList();
+ }
+
+ public static abstract class TvInputManager.TvInputListener {
+ ctor public TvInputManager.TvInputListener();
+ method public void onAvailabilityChanged(java.lang.String, boolean);
+ }
+
+ public abstract class TvInputService extends android.app.Service {
+ ctor public TvInputService();
+ method public final android.os.IBinder onBind(android.content.Intent);
+ method public abstract android.media.tv.TvInputService.Session onCreateSession();
+ field public static final java.lang.String SERVICE_INTERFACE = "android.media.tv.TvInputService";
+ field public static final java.lang.String SERVICE_META_DATA = "android.media.tv.input";
+ }
+
+ public abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
+ ctor public TvInputService.Session();
+ method public android.view.View onCreateOverlayView();
+ method public boolean onGenericMotionEvent(android.view.MotionEvent);
+ method public boolean onKeyDown(int, android.view.KeyEvent);
+ method public boolean onKeyLongPress(int, android.view.KeyEvent);
+ method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
+ method public boolean onKeyUp(int, android.view.KeyEvent);
+ method public abstract void onRelease();
+ method public abstract void onSetStreamVolume(float);
+ method public abstract boolean onSetSurface(android.view.Surface);
+ method public boolean onTouchEvent(android.view.MotionEvent);
+ method public boolean onTrackballEvent(android.view.MotionEvent);
+ method public abstract boolean onTune(android.net.Uri);
+ method public void setOverlayViewEnabled(boolean);
+ }
+
+ public class TvView extends android.view.ViewGroup {
+ ctor public TvView(android.content.Context);
+ ctor public TvView(android.content.Context, android.util.AttributeSet);
+ ctor public TvView(android.content.Context, android.util.AttributeSet, int);
+ method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
+ method protected void onLayout(boolean, int, int, int, int);
+ method public boolean onUnhandledInputEvent(android.view.InputEvent);
+ method public void reset();
+ method public void setOnUnhandledInputEventListener(android.media.tv.TvView.OnUnhandledInputEventListener);
+ method public void setStreamVolume(float);
+ method public void setTvInputListener(android.media.tv.TvView.TvInputListener);
+ method public void tune(java.lang.String, android.net.Uri);
+ field public static final int ERROR_BUSY = 0; // 0x0
+ field public static final int ERROR_TV_INPUT_DISCONNECTED = 1; // 0x1
+ }
+
+ public static abstract interface TvView.OnUnhandledInputEventListener {
+ method public abstract boolean onUnhandledInputEvent(android.view.InputEvent);
+ }
+
+ public static abstract class TvView.TvInputListener {
+ ctor public TvView.TvInputListener();
+ method public void onError(java.lang.String, int);
+ }
+
+}
+
package android.mtp {
public final class MtpConstants {
@@ -16115,28 +16097,27 @@
method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
method public android.net.NetworkInfo getNetworkInfo(int);
method public deprecated int getNetworkPreference();
+ method public static android.net.Network getProcessDefaultNetwork();
method public boolean isActiveNetworkMetered();
- method public boolean isNetworkActive();
+ method public boolean isDefaultNetworkActive();
method public static boolean isNetworkTypeValid(int);
- method public android.net.NetworkRequest listenForNetwork(android.net.NetworkCapabilities, android.net.ConnectivityManager.NetworkCallbackListener);
- method public void registerNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
- method public void releaseNetworkRequest(android.net.NetworkRequest);
+ method public void registerDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
+ method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public void reportBadNetwork(android.net.Network);
- method public android.net.NetworkRequest requestNetwork(android.net.NetworkCapabilities, android.net.ConnectivityManager.NetworkCallbackListener);
- method public android.net.NetworkRequest requestNetwork(android.net.NetworkCapabilities, android.app.PendingIntent);
+ method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
+ method public static boolean setProcessDefaultNetwork(android.net.Network);
method public deprecated int startUsingNetworkFeature(int, java.lang.String);
method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
- method public void unregisterNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
+ method public void unregisterDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
+ method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
- field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
- field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES = "networkRequestNetworkCapabilities";
field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType";
field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity";
field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
@@ -16153,14 +16134,13 @@
field public static final int TYPE_WIMAX = 6; // 0x6
}
- public static class ConnectivityManager.NetworkCallbackListener {
- ctor public ConnectivityManager.NetworkCallbackListener();
- method public void onAvailable(android.net.NetworkRequest, android.net.Network);
- method public void onLinkPropertiesChanged(android.net.NetworkRequest, android.net.Network, android.net.LinkProperties);
- method public void onLosing(android.net.NetworkRequest, android.net.Network, int);
- method public void onLost(android.net.NetworkRequest, android.net.Network);
- method public void onNetworkCapabilitiesChanged(android.net.NetworkRequest, android.net.Network, android.net.NetworkCapabilities);
- method public void onReleased(android.net.NetworkRequest);
+ public static class ConnectivityManager.NetworkCallback {
+ ctor public ConnectivityManager.NetworkCallback();
+ method public void onAvailable(android.net.Network);
+ method public void onCapabilitiesChanged(android.net.Network, android.net.NetworkCapabilities);
+ method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
+ method public void onLosing(android.net.Network, int);
+ method public void onLost(android.net.Network);
}
public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -16187,46 +16167,34 @@
field public int serverAddress;
}
- public class LinkAddress implements android.os.Parcelable {
+ public final class IpPrefix implements android.os.Parcelable {
method public int describeContents();
method public java.net.InetAddress getAddress();
- method public int getFlags();
- method public int getNetworkPrefixLength();
- method public int getScope();
- method public boolean isSameAddressAs(android.net.LinkAddress);
- method public void writeToParcel(android.os.Parcel, int);
- }
-
- public class LinkProperties implements android.os.Parcelable {
- ctor public LinkProperties();
- ctor public LinkProperties(android.net.LinkProperties);
- method public void addDns(java.net.InetAddress);
- method public boolean addLinkAddress(android.net.LinkAddress);
- method public void addRoute(android.net.RouteInfo);
- method public void clear();
- method public int describeContents();
- method public java.util.Collection<java.lang.String> getAllInterfaceNames();
- method public java.util.Collection<java.net.InetAddress> getDnses();
- method public java.lang.String getDomains();
- method public android.net.ProxyInfo getHttpProxy();
- method public java.lang.String getInterfaceName();
- method public java.util.Collection<android.net.LinkAddress> getLinkAddresses();
- method public java.util.Collection<android.net.RouteInfo> getRoutes();
- method public boolean hasIPv4Address();
- method public boolean hasIPv6Address();
- method public boolean removeLinkAddress(android.net.LinkAddress);
- method public void setDomains(java.lang.String);
- method public void setHttpProxy(android.net.ProxyInfo);
- method public void setInterfaceName(java.lang.String);
- method public void setLinkAddresses(java.util.Collection<android.net.LinkAddress>);
+ method public int getPrefixLength();
+ method public byte[] getRawAddress();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
- public static class LinkProperties.CompareResult {
- ctor public LinkProperties.CompareResult();
- field public java.util.Collection added;
- field public java.util.Collection removed;
+ public class LinkAddress implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.net.InetAddress getAddress();
+ method public int getFlags();
+ method public int getPrefixLength();
+ method public int getScope();
+ method public void writeToParcel(android.os.Parcel, int);
+ }
+
+ public final class LinkProperties implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.List<java.net.InetAddress> getDnsServers();
+ method public java.lang.String getDomains();
+ method public android.net.ProxyInfo getHttpProxy();
+ method public java.lang.String getInterfaceName();
+ method public java.util.List<android.net.LinkAddress> getLinkAddresses();
+ method public java.util.List<android.net.RouteInfo> getRoutes();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
}
public class LocalServerSocket {
@@ -16298,33 +16266,21 @@
}
public class Network implements android.os.Parcelable {
- method public void bindProcess();
method public int describeContents();
method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
- method public static android.net.Network getProcessBoundNetwork();
- method public javax.net.SocketFactory socketFactory();
- method public static void unbindProcess();
+ method public javax.net.SocketFactory getSocketFactory();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public final class NetworkCapabilities implements android.os.Parcelable {
- ctor public NetworkCapabilities();
ctor public NetworkCapabilities(android.net.NetworkCapabilities);
- method public void addNetworkCapability(int);
- method public void addTransportType(int);
method public int describeContents();
method public int getLinkDownstreamBandwidthKbps();
method public int getLinkUpstreamBandwidthKbps();
- method public java.util.Collection<java.lang.Integer> getNetworkCapabilities();
- method public java.util.Collection<java.lang.Integer> getTransportTypes();
method public boolean hasCapability(int);
method public boolean hasTransport(int);
- method public void removeNetworkCapability(int);
- method public void removeTransportType(int);
- method public void setLinkDownstreamBandwidthKbps(int);
- method public void setLinkUpstreamBandwidthKbps(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int NET_CAPABILITY_CBS = 5; // 0x5
@@ -16398,7 +16354,15 @@
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
- field public final android.net.NetworkCapabilities networkCapabilities;
+ }
+
+ public static class NetworkRequest.Builder {
+ ctor public NetworkRequest.Builder();
+ method public android.net.NetworkRequest.Builder addCapability(int);
+ method public android.net.NetworkRequest.Builder addTransportType(int);
+ method public android.net.NetworkRequest build();
+ method public android.net.NetworkRequest.Builder removeCapability(int);
+ method public android.net.NetworkRequest.Builder removeTransportType(int);
}
public class ParseException extends java.lang.RuntimeException {
@@ -16427,18 +16391,15 @@
method public void writeToParcel(android.os.Parcel, int);
}
- public class RouteInfo implements android.os.Parcelable {
- ctor public RouteInfo(android.net.LinkAddress, java.net.InetAddress, java.lang.String);
- ctor public RouteInfo(android.net.LinkAddress, java.net.InetAddress);
- ctor public RouteInfo(java.net.InetAddress);
- ctor public RouteInfo(android.net.LinkAddress);
+ public final class RouteInfo implements android.os.Parcelable {
method public int describeContents();
- method public android.net.LinkAddress getDestination();
+ method public android.net.IpPrefix getDestination();
method public java.net.InetAddress getGateway();
method public java.lang.String getInterface();
method public boolean isDefaultRoute();
method public boolean matches(java.net.InetAddress);
method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
}
public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
@@ -20528,7 +20489,8 @@
public class BatteryManager {
ctor public BatteryManager();
- method public android.os.BatteryProperty getProperty(int) throws android.os.RemoteException;
+ method public int getIntProperty(int);
+ method public long getLongProperty(int);
field public static final int BATTERY_HEALTH_COLD = 7; // 0x7
field public static final int BATTERY_HEALTH_DEAD = 4; // 0x4
field public static final int BATTERY_HEALTH_GOOD = 2; // 0x2
@@ -20539,6 +20501,11 @@
field public static final int BATTERY_PLUGGED_AC = 1; // 0x1
field public static final int BATTERY_PLUGGED_USB = 2; // 0x2
field public static final int BATTERY_PLUGGED_WIRELESS = 4; // 0x4
+ field public static final int BATTERY_PROPERTY_CAPACITY = 4; // 0x4
+ field public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1; // 0x1
+ field public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3; // 0x3
+ field public static final int BATTERY_PROPERTY_CURRENT_NOW = 2; // 0x2
+ field public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; // 0x5
field public static final int BATTERY_STATUS_CHARGING = 2; // 0x2
field public static final int BATTERY_STATUS_DISCHARGING = 3; // 0x3
field public static final int BATTERY_STATUS_FULL = 5; // 0x5
@@ -20556,20 +20523,6 @@
field public static final java.lang.String EXTRA_VOLTAGE = "voltage";
}
- public class BatteryProperty implements android.os.Parcelable {
- method public int describeContents();
- method public int getInt();
- method public long getLong();
- method public void readFromParcel(android.os.Parcel);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CAPACITY = 4; // 0x4
- field public static final int CHARGE_COUNTER = 1; // 0x1
- field public static final android.os.Parcelable.Creator CREATOR;
- field public static final int CURRENT_AVERAGE = 3; // 0x3
- field public static final int CURRENT_NOW = 2; // 0x2
- field public static final int ENERGY_COUNTER = 5; // 0x5
- }
-
public class Binder implements android.os.IBinder {
ctor public Binder();
method public void attachInterface(android.os.IInterface, java.lang.String);
@@ -21329,12 +21282,14 @@
public final class PowerManager {
method public void goToSleep(long);
method public boolean isInteractive();
+ method public boolean isPowerSaveMode();
method public deprecated boolean isScreenOn();
method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
method public void reboot(java.lang.String);
method public void userActivity(long, boolean);
method public void wakeUp(long);
field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+ field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
field public static final int PARTIAL_WAKE_LOCK = 1; // 0x1
@@ -21549,6 +21504,7 @@
method public java.util.List<android.os.UserHandle> getUserProfiles();
method public android.os.Bundle getUserRestrictions();
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+ method public boolean hasUserRestriction(java.lang.String);
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
@@ -22354,6 +22310,7 @@
method protected void onDisconnected();
method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
method protected abstract void onRequestCancelPrintJob(android.printservice.PrintJob);
+ field public static final java.lang.String EXTRA_PRINTER_INFO = "android.intent.extra.print.PRINTER_INFO";
field public static final java.lang.String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
@@ -22868,16 +22825,6 @@
field public static final deprecated android.net.Uri DELETED_CONTENT_URI;
field public static final deprecated java.lang.String GROUP_ANDROID_STARRED = "Starred in Android";
field public static final deprecated java.lang.String GROUP_MY_CONTACTS = "Contacts";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT = "non_syncable";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT_TYPE = "android.local";
- field public static final java.lang.String _SYNC_ACCOUNT = "_sync_account";
- field public static final java.lang.String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
- field public static final java.lang.String _SYNC_DIRTY = "_sync_dirty";
- field public static final java.lang.String _SYNC_ID = "_sync_id";
- field public static final java.lang.String _SYNC_LOCAL_ID = "_sync_local_id";
- field public static final java.lang.String _SYNC_MARK = "_sync_mark";
- field public static final java.lang.String _SYNC_TIME = "_sync_time";
- field public static final java.lang.String _SYNC_VERSION = "_sync_version";
}
public static abstract deprecated interface Contacts.GroupsColumns {
@@ -22979,19 +22926,9 @@
field public static final deprecated android.net.Uri CONTENT_URI;
field public static final deprecated java.lang.String DEFAULT_SORT_ORDER = "name ASC";
field public static final deprecated android.net.Uri DELETED_CONTENT_URI;
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT = "non_syncable";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT_TYPE = "android.local";
field public static final deprecated java.lang.String PRIMARY_EMAIL_ID = "primary_email";
field public static final deprecated java.lang.String PRIMARY_ORGANIZATION_ID = "primary_organization";
field public static final deprecated java.lang.String PRIMARY_PHONE_ID = "primary_phone";
- field public static final java.lang.String _SYNC_ACCOUNT = "_sync_account";
- field public static final java.lang.String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
- field public static final java.lang.String _SYNC_DIRTY = "_sync_dirty";
- field public static final java.lang.String _SYNC_ID = "_sync_id";
- field public static final java.lang.String _SYNC_LOCAL_ID = "_sync_local_id";
- field public static final java.lang.String _SYNC_MARK = "_sync_mark";
- field public static final java.lang.String _SYNC_TIME = "_sync_time";
- field public static final java.lang.String _SYNC_VERSION = "_sync_version";
}
public static final deprecated class Contacts.People.ContactMethods implements android.provider.BaseColumns android.provider.Contacts.ContactMethodsColumns android.provider.Contacts.PeopleColumns {
@@ -23054,16 +22991,6 @@
field public static final deprecated java.lang.String CONTENT_DIRECTORY = "photo";
field public static final deprecated android.net.Uri CONTENT_URI;
field public static final deprecated java.lang.String DEFAULT_SORT_ORDER = "person ASC";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT = "non_syncable";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT_TYPE = "android.local";
- field public static final java.lang.String _SYNC_ACCOUNT = "_sync_account";
- field public static final java.lang.String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
- field public static final java.lang.String _SYNC_DIRTY = "_sync_dirty";
- field public static final java.lang.String _SYNC_ID = "_sync_id";
- field public static final java.lang.String _SYNC_LOCAL_ID = "_sync_local_id";
- field public static final java.lang.String _SYNC_MARK = "_sync_mark";
- field public static final java.lang.String _SYNC_TIME = "_sync_time";
- field public static final java.lang.String _SYNC_VERSION = "_sync_version";
}
public static abstract deprecated interface Contacts.PhotosColumns {
@@ -23391,7 +23318,6 @@
protected static abstract interface ContactsContract.ContactOptionsColumns {
field public static final java.lang.String CUSTOM_RINGTONE = "custom_ringtone";
field public static final java.lang.String LAST_TIME_CONTACTED = "last_time_contacted";
- field public static final java.lang.String PINNED = "pinned";
field public static final java.lang.String SEND_TO_VOICEMAIL = "send_to_voicemail";
field public static final java.lang.String STARRED = "starred";
field public static final java.lang.String TIMES_CONTACTED = "times_contacted";
@@ -23669,15 +23595,6 @@
field public static final int UNDEFINED = 0; // 0x0
}
- public static final class ContactsContract.PinnedPositions {
- ctor public ContactsContract.PinnedPositions();
- field public static final int DEMOTED = -1; // 0xffffffff
- field public static final java.lang.String STAR_WHEN_PINNING = "star_when_pinning";
- field public static final java.lang.String UNDEMOTE = "undemote";
- field public static final int UNPINNED = 2147483647; // 0x7fffffff
- field public static final android.net.Uri UPDATE_URI;
- }
-
public static final class ContactsContract.Preferences {
ctor public ContactsContract.Preferences();
field public static final java.lang.String DISPLAY_ORDER = "android.contacts.DISPLAY_ORDER";
@@ -23886,21 +23803,21 @@
public final class DocumentsContract {
method public static android.net.Uri buildChildDocumentsUri(java.lang.String, java.lang.String);
- method public static android.net.Uri buildChildDocumentsViaUri(android.net.Uri, java.lang.String);
+ method public static android.net.Uri buildChildDocumentsUriUsingTree(android.net.Uri, java.lang.String);
method public static android.net.Uri buildDocumentUri(java.lang.String, java.lang.String);
- method public static android.net.Uri buildDocumentViaUri(android.net.Uri, java.lang.String);
+ method public static android.net.Uri buildDocumentUriUsingTree(android.net.Uri, java.lang.String);
method public static android.net.Uri buildRecentDocumentsUri(java.lang.String, java.lang.String);
method public static android.net.Uri buildRootUri(java.lang.String, java.lang.String);
method public static android.net.Uri buildRootsUri(java.lang.String);
method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
- method public static android.net.Uri buildViaUri(java.lang.String, java.lang.String);
+ method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String);
method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri);
method public static java.lang.String getDocumentId(android.net.Uri);
method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal);
method public static java.lang.String getRootId(android.net.Uri);
method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
- method public static java.lang.String getViaDocumentId(android.net.Uri);
+ method public static java.lang.String getTreeDocumentId(android.net.Uri);
method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String);
field public static final java.lang.String EXTRA_ERROR = "error";
@@ -23939,7 +23856,7 @@
field public static final java.lang.String COLUMN_TITLE = "title";
field public static final int FLAG_LOCAL_ONLY = 2; // 0x2
field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
- field public static final int FLAG_SUPPORTS_DIR_SELECTION = 16; // 0x10
+ field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10
field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4
field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8
}
@@ -24160,7 +24077,6 @@
}
public static final class MediaStore.Audio.Radio {
- ctor public MediaStore.Audio.Radio();
field public static final java.lang.String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
}
@@ -24945,81 +24861,6 @@
field public static final java.lang.String TYPE = "type";
}
- public final class TvContract {
- method public static final android.net.Uri buildChannelUri(long);
- method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName);
- method public static final android.net.Uri buildChannelsUriForInput(android.content.ComponentName, boolean);
- method public static final android.net.Uri buildProgramUri(long);
- method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri);
- method public static final android.net.Uri buildProgramsUriForChannel(android.net.Uri, long, long);
- field public static final java.lang.String AUTHORITY = "com.android.tv";
- }
-
- public static abstract interface TvContract.BaseTvColumns implements android.provider.BaseColumns {
- field public static final java.lang.String COLUMN_PACKAGE_NAME = "package_name";
- }
-
- public static final class TvContract.Channels implements android.provider.TvContract.BaseTvColumns {
- field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
- field public static final java.lang.String COLUMN_DATA = "data";
- field public static final java.lang.String COLUMN_DESCRIPTION = "description";
- field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
- field public static final java.lang.String COLUMN_DISPLAY_NUMBER = "display_number";
- field public static final java.lang.String COLUMN_LOCKED = "locked";
- field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
- field public static final java.lang.String COLUMN_SEARCHABLE = "searchable";
- field public static final java.lang.String COLUMN_SERVICE_ID = "service_id";
- field public static final java.lang.String COLUMN_SERVICE_NAME = "service_name";
- field public static final java.lang.String COLUMN_SERVICE_TYPE = "service_type";
- field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
- field public static final java.lang.String COLUMN_TYPE = "type";
- field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
- field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.android.tv.channels";
- field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.android.tv.channels";
- field public static final android.net.Uri CONTENT_URI;
- field public static final int SERVICE_TYPE_OTHER = 0; // 0x0
- field public static final int SERVICE_TYPE_RADIO = 2; // 0x2
- field public static final int SERVICE_TYPE_TV = 1; // 0x1
- field public static final int TYPE_1SEG = 263168; // 0x40400
- field public static final int TYPE_ATSC_C = 197120; // 0x30200
- field public static final int TYPE_ATSC_M_H = 197120; // 0x30200
- field public static final int TYPE_ATSC_T = 196608; // 0x30000
- field public static final int TYPE_CMMB = 327936; // 0x50100
- field public static final int TYPE_DTMB = 327680; // 0x50000
- field public static final int TYPE_DVB_C = 131584; // 0x20200
- field public static final int TYPE_DVB_C2 = 131585; // 0x20201
- field public static final int TYPE_DVB_H = 131840; // 0x20300
- field public static final int TYPE_DVB_S = 131328; // 0x20100
- field public static final int TYPE_DVB_S2 = 131329; // 0x20101
- field public static final int TYPE_DVB_SH = 132096; // 0x20400
- field public static final int TYPE_DVB_T = 131072; // 0x20000
- field public static final int TYPE_DVB_T2 = 131073; // 0x20001
- field public static final int TYPE_ISDB_C = 262912; // 0x40300
- field public static final int TYPE_ISDB_S = 262656; // 0x40200
- field public static final int TYPE_ISDB_T = 262144; // 0x40000
- field public static final int TYPE_ISDB_TB = 262400; // 0x40100
- field public static final int TYPE_OTHER = 0; // 0x0
- field public static final int TYPE_PASSTHROUGH = 65536; // 0x10000
- field public static final int TYPE_S_DMB = 393472; // 0x60100
- field public static final int TYPE_T_DMB = 393216; // 0x60000
- }
-
- public static final class TvContract.Programs implements android.provider.TvContract.BaseTvColumns {
- field public static final java.lang.String COLUMN_AUDIO_LANGUAGE = "audio_language";
- field public static final java.lang.String COLUMN_CHANNEL_ID = "channel_id";
- field public static final java.lang.String COLUMN_DATA = "data";
- field public static final java.lang.String COLUMN_DESCRIPTION = "description";
- field public static final java.lang.String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
- field public static final java.lang.String COLUMN_GENRE = "genre";
- field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
- field public static final java.lang.String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
- field public static final java.lang.String COLUMN_TITLE = "title";
- field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
- field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.com.android.tv.programs";
- field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.com.android.tv.programs";
- field public static final android.net.Uri CONTENT_URI;
- }
-
public class UserDictionary {
ctor public UserDictionary();
field public static final java.lang.String AUTHORITY = "user_dictionary";
@@ -25618,7 +25459,7 @@
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 static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
method public void destroy();
method public void finish();
method public final android.content.Context getApplicationContext();
@@ -25628,9 +25469,9 @@
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 = 2L; // 0x2L
- field public static final long CREATE_FLAG_LOW_POWER = 4L; // 0x4L
- field public static final long CREATE_FLAG_NONE = 0L; // 0x0L
+ field public static final int CREATE_FLAG_LOW_LATENCY = 2; // 0x2
+ field public static final int CREATE_FLAG_LOW_POWER = 4; // 0x4
+ field public static final int CREATE_FLAG_NONE = 0; // 0x0
}
public static final class RenderScript.ContextType extends java.lang.Enum {
@@ -26183,21 +26024,28 @@
method public final void cancelNotification(java.lang.String);
method public final void cancelNotifications(java.lang.String[]);
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
- method public android.service.notification.NotificationListenerService.Ranking getCurrentRanking();
+ method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
method public android.os.IBinder onBind(android.content.Intent);
method public void onListenerConnected();
- method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
- method public void onNotificationRankingUpdate();
- method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
+ method public void onNotificationPosted(android.service.notification.StatusBarNotification);
+ method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
+ method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
+ method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
+ method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
}
- public static class NotificationListenerService.Ranking implements android.os.Parcelable {
+ public static class NotificationListenerService.Ranking {
+ method public java.lang.String getKey();
+ method public int getRank();
+ method public boolean isAmbient();
+ method public boolean isInterceptedByDoNotDisturb();
+ }
+
+ public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
method public int describeContents();
method public java.lang.String[] getOrderedKeys();
- method public int getRank(java.lang.String);
- method public boolean isAmbient(java.lang.String);
- method public boolean isInterceptedByDoNotDisturb(java.lang.String);
+ method public android.service.notification.NotificationListenerService.Ranking getRanking(java.lang.String);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -26656,6 +26504,7 @@
method public void disconnect();
method public android.speech.tts.TextToSpeechClient.EngineStatus getEngineStatus();
method public boolean isConnected();
+ method public boolean isSpeaking();
method public void queueAudio(android.net.Uri, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
method public void queueSilence(long, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.TextToSpeechClient.RequestCallbacks);
method public void queueSpeak(java.lang.String, android.speech.tts.TextToSpeechClient.UtteranceId, android.speech.tts.RequestConfig, android.speech.tts.TextToSpeechClient.RequestCallbacks);
@@ -27510,8 +27359,8 @@
field public final int supportedRouteMask;
}
- public class CallCapabilities {
- ctor public CallCapabilities();
+ public final class CallCapabilities {
+ method public static java.lang.String toString(int);
field public static final int ADD_CALL = 16; // 0x10
field public static final int ALL = 511; // 0x1ff
field public static final int CONNECTION_HANDOFF = 256; // 0x100
@@ -27560,6 +27409,8 @@
method protected void onAdapterAttached(android.telecomm.CallServiceAdapter);
method public abstract void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
method public final android.os.IBinder onBind(android.content.Intent);
+ method public void onPostDialContinue(java.lang.String, boolean);
+ method public void onPostDialWait(android.telecomm.Connection, java.lang.String);
method public abstract void playDtmfTone(java.lang.String, char);
method public abstract void reject(java.lang.String);
method public abstract void setIncomingCallId(java.lang.String, android.os.Bundle);
@@ -27568,9 +27419,12 @@
}
public final class CallServiceAdapter {
- method public void handleFailedOutgoingCall(java.lang.String, java.lang.String);
+ method public void addConferenceCall(java.lang.String);
+ method public void handleFailedOutgoingCall(android.telecomm.ConnectionRequest, int, java.lang.String);
method public void handleSuccessfulOutgoingCall(java.lang.String);
+ method public void handoffCall(java.lang.String);
method public void notifyIncomingCall(android.telecomm.CallInfo);
+ method public void onPostDialWait(java.lang.String, java.lang.String);
method public void setActive(java.lang.String);
method public void setDialing(java.lang.String);
method public void setDisconnected(java.lang.String, int, java.lang.String);
@@ -27640,15 +27494,23 @@
public abstract class Connection {
ctor protected Connection();
+ method public final void conference();
method public final android.telecomm.CallAudioState getCallAudioState();
+ method public java.util.List<android.telecomm.Connection> getChildConnections();
method public final android.net.Uri getHandle();
+ method public android.telecomm.Connection getParentConnection();
+ method public boolean isConferenceConnection();
method public boolean isRequestingRingback();
method protected void onAbort();
method protected void onAnswer();
+ method protected void onChildrenChanged(java.util.List<android.telecomm.Connection>);
+ method protected void onConference();
method protected void onDisconnect();
method protected void onHold();
method protected void onPlayDtmfTone(char);
+ method protected void onPostDialContinue(boolean);
method protected void onReject();
+ method protected void onSeparate();
method protected void onSetAudioState(android.telecomm.CallAudioState);
method protected void onSetSignal(android.os.Bundle);
method protected void onSetState(int);
@@ -27656,10 +27518,13 @@
method protected void onUnhold();
method protected void setActive();
method public void setAudioState(android.telecomm.CallAudioState);
+ method protected void setDestroyed();
method protected void setDialing();
method protected void setDisconnected(int, java.lang.String);
method protected void setHandle(android.net.Uri);
+ method protected void setIsConferenceCapable(boolean);
method protected void setOnHold();
+ method public void setParentConnection(android.telecomm.Connection);
method protected void setRequestingRingback(boolean);
method protected void setRinging();
method public static java.lang.String stateToString(int);
@@ -27667,9 +27532,11 @@
public static abstract interface Connection.Listener {
method public abstract void onAudioStateChanged(android.telecomm.Connection, android.telecomm.CallAudioState);
+ method public abstract void onConferenceCapableChanged(android.telecomm.Connection, boolean);
method public abstract void onDestroyed(android.telecomm.Connection);
method public abstract void onDisconnected(android.telecomm.Connection, int, java.lang.String);
method public abstract void onHandleChanged(android.telecomm.Connection, android.net.Uri);
+ method public abstract void onParentConnectionChanged(android.telecomm.Connection, android.telecomm.Connection);
method public abstract void onRequestingRingback(android.telecomm.Connection, boolean);
method public abstract void onSignalChanged(android.telecomm.Connection, android.os.Bundle);
method public abstract void onStateChanged(android.telecomm.Connection, int);
@@ -27678,9 +27545,11 @@
public static class Connection.ListenerBase implements android.telecomm.Connection.Listener {
ctor public Connection.ListenerBase();
method public void onAudioStateChanged(android.telecomm.Connection, android.telecomm.CallAudioState);
+ method public void onConferenceCapableChanged(android.telecomm.Connection, boolean);
method public void onDestroyed(android.telecomm.Connection);
method public void onDisconnected(android.telecomm.Connection, int, java.lang.String);
method public void onHandleChanged(android.telecomm.Connection, android.net.Uri);
+ method public void onParentConnectionChanged(android.telecomm.Connection, android.telecomm.Connection);
method public void onRequestingRingback(android.telecomm.Connection, boolean);
method public void onSignalChanged(android.telecomm.Connection, android.os.Bundle);
method public void onStateChanged(android.telecomm.Connection, int);
@@ -27695,10 +27564,15 @@
field public static final int RINGING = 1; // 0x1
}
- public final class ConnectionRequest {
+ public final class ConnectionRequest implements android.os.Parcelable {
ctor public ConnectionRequest(android.net.Uri, android.os.Bundle);
+ ctor public ConnectionRequest(java.lang.String, android.net.Uri, android.os.Bundle);
+ method public int describeContents();
+ method public java.lang.String getCallId();
method public android.os.Bundle getExtras();
method public android.net.Uri getHandle();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
}
public abstract class ConnectionService extends android.telecomm.CallService {
@@ -27707,12 +27581,18 @@
method public final void answer(java.lang.String);
method public final void call(android.telecomm.CallInfo);
method public final void disconnect(java.lang.String);
+ method public java.util.Collection<android.telecomm.Connection> getAllConnections();
method public final void hold(java.lang.String);
method public final void isCompatibleWith(android.telecomm.CallInfo);
method public final void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
+ method public void onConnectionAdded(android.telecomm.Connection);
+ method public void onConnectionRemoved(android.telecomm.Connection);
+ method public void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
method public void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
method public void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
method public void onFindSubscriptions(android.net.Uri, android.telecomm.Response<android.net.Uri, android.telecomm.Subscription>);
+ method public final void onPostDialContinue(java.lang.String, boolean);
+ method public final void onPostDialWait(android.telecomm.Connection, java.lang.String);
method public final void playDtmfTone(java.lang.String, char);
method public final void reject(java.lang.String);
method public final void setIncomingCallId(java.lang.String, android.os.Bundle);
@@ -27737,7 +27617,7 @@
method public void holdCall(java.lang.String);
method public void mute(boolean);
method public void playDtmfTone(java.lang.String, char);
- method public void postDialContinue(java.lang.String);
+ method public void postDialContinue(java.lang.String, boolean);
method public void rejectCall(java.lang.String);
method public void setAudioRoute(int);
method public void stopDtmfTone(java.lang.String);
@@ -27749,7 +27629,8 @@
method public int getCapabilities();
method public long getConnectTimeMillis();
method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
- method public int getDisconnectCause();
+ method public int getDisconnectCauseCode();
+ method public java.lang.String getDisconnectCauseMsg();
method public android.telecomm.GatewayInfo getGatewayInfo();
method public android.net.Uri getHandle();
method public android.telecomm.CallServiceDescriptor getHandoffCallServiceDescriptor();
@@ -27773,7 +27654,7 @@
}
public abstract interface Response {
- method public abstract void onError(IN, java.lang.String);
+ method public abstract void onError(IN, int, java.lang.String);
method public abstract void onResult(IN, OUT...);
}
@@ -27954,6 +27835,7 @@
field public static final int CALL_BARRED = 20; // 0x14
field public static final int CDMA_ACCESS_BLOCKED = 35; // 0x23
field public static final int CDMA_ACCESS_FAILURE = 32; // 0x20
+ field public static final int CDMA_CALL_LOST = 41; // 0x29
field public static final int CDMA_DROP = 27; // 0x1b
field public static final int CDMA_INTERCEPT = 28; // 0x1c
field public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26; // 0x1a
@@ -27966,6 +27848,8 @@
field public static final int CS_RESTRICTED = 22; // 0x16
field public static final int CS_RESTRICTED_EMERGENCY = 24; // 0x18
field public static final int CS_RESTRICTED_NORMAL = 23; // 0x17
+ field public static final int DIALED_MMI = 39; // 0x27
+ field public static final int EMERGENCY_ONLY = 37; // 0x25
field public static final int ERROR_UNSPECIFIED = 36; // 0x24
field public static final int FDN_BLOCKED = 21; // 0x15
field public static final int ICC_ERROR = 19; // 0x13
@@ -27976,12 +27860,13 @@
field public static final int LIMIT_EXCEEDED = 15; // 0xf
field public static final int LOCAL = 3; // 0x3
field public static final int LOST_SIGNAL = 14; // 0xe
- field public static final int MAXIMUM_VALID_VALUE = 36; // 0x24
+ field public static final int MAXIMUM_VALID_VALUE = 42; // 0x2a
field public static final int MINIMUM_VALID_VALUE = 0; // 0x0
field public static final int MMI = 6; // 0x6
field public static final int NORMAL = 2; // 0x2
field public static final int NOT_DISCONNECTED = 0; // 0x0
field public static final int NOT_VALID = -1; // 0xffffffff
+ field public static final int NO_PHONE_NUMBER_SUPPLIED = 38; // 0x26
field public static final int NUMBER_UNREACHABLE = 8; // 0x8
field public static final int OUT_OF_NETWORK = 11; // 0xb
field public static final int OUT_OF_SERVICE = 18; // 0x12
@@ -27990,6 +27875,7 @@
field public static final int SERVER_UNREACHABLE = 9; // 0x9
field public static final int TIMED_OUT = 13; // 0xd
field public static final int UNOBTAINABLE_NUMBER = 25; // 0x19
+ field public static final int VOICEMAIL_NUMBER_MISSING = 40; // 0x28
}
public class NeighboringCellInfo implements android.os.Parcelable {
@@ -28043,7 +27929,7 @@
method public static boolean isEmergencyNumber(java.lang.String);
method public static boolean isGlobalPhoneNumber(java.lang.String);
method public static boolean isISODigit(char);
- method public static boolean isLocalEmergencyNumber(java.lang.String, android.content.Context);
+ method public static boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
method public static final boolean isNonSeparator(char);
method public static final boolean isReallyDialable(char);
method public static final boolean isStartsPostDial(char);
@@ -30452,20 +30338,12 @@
method public long getStartDelay(android.view.ViewGroup, android.transition.Transition, android.transition.TransitionValues, android.transition.TransitionValues);
method public void setPropagationSpeed(float);
method public void setSide(int);
- field public static final int BOTTOM = 3; // 0x3
- field public static final int LEFT = 0; // 0x0
- field public static final int RIGHT = 2; // 0x2
- field public static final int TOP = 1; // 0x1
}
public class Slide extends android.transition.Visibility {
ctor public Slide();
ctor public Slide(int);
method public void setSlideEdge(int);
- field public static final int BOTTOM = 3; // 0x3
- field public static final int LEFT = 0; // 0x0
- field public static final int RIGHT = 2; // 0x2
- field public static final int TOP = 1; // 0x1
}
public abstract class Transition implements java.lang.Cloneable {
@@ -30519,7 +30397,7 @@
public static abstract class Transition.EpicenterCallback {
ctor public Transition.EpicenterCallback();
- method public abstract android.graphics.Rect getEpicenter(android.transition.Transition);
+ method public abstract android.graphics.Rect onGetEpicenter(android.transition.Transition);
}
public static abstract interface Transition.TransitionListener {
@@ -30594,85 +30472,6 @@
}
-package android.tv {
-
- public final class TvInputInfo implements android.os.Parcelable {
- method public int describeContents();
- method public android.content.ComponentName getComponent();
- method public java.lang.String getId();
- method public java.lang.String getPackageName();
- method public java.lang.String getServiceName();
- method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
- method public void writeToParcel(android.os.Parcel, int);
- }
-
- public final class TvInputManager {
- method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCallback, android.os.Handler);
- method public boolean getAvailability(java.lang.String);
- method public java.util.List<android.tv.TvInputInfo> getTvInputList();
- method public void registerListener(java.lang.String, android.tv.TvInputManager.TvInputListener, android.os.Handler);
- method public void unregisterListener(java.lang.String, android.tv.TvInputManager.TvInputListener);
- }
-
- public static final class TvInputManager.Session {
- method public void release();
- method public void setVolume(float);
- method public void tune(android.net.Uri);
- }
-
- public static abstract class TvInputManager.SessionCallback {
- ctor public TvInputManager.SessionCallback();
- method public void onSessionCreated(android.tv.TvInputManager.Session);
- method public void onSessionReleased(android.tv.TvInputManager.Session);
- }
-
- public static abstract class TvInputManager.TvInputListener {
- ctor public TvInputManager.TvInputListener();
- method public void onAvailabilityChanged(java.lang.String, boolean);
- }
-
- public abstract class TvInputService extends android.app.Service {
- ctor public TvInputService();
- method public final android.os.IBinder onBind(android.content.Intent);
- method public abstract android.tv.TvInputService.TvInputSessionImpl onCreateSession();
- method public final void setAvailable(boolean);
- field public static final java.lang.String SERVICE_INTERFACE = "android.tv.TvInputService";
- }
-
- public abstract class TvInputService.TvInputSessionImpl implements android.view.KeyEvent.Callback {
- ctor public TvInputService.TvInputSessionImpl();
- method public android.view.View onCreateOverlayView();
- method public boolean onGenericMotionEvent(android.view.MotionEvent);
- method public boolean onKeyDown(int, android.view.KeyEvent);
- method public boolean onKeyLongPress(int, android.view.KeyEvent);
- method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
- method public boolean onKeyUp(int, android.view.KeyEvent);
- method public abstract void onRelease();
- method public abstract boolean onSetSurface(android.view.Surface);
- method public abstract void onSetVolume(float);
- method public boolean onTouchEvent(android.view.MotionEvent);
- method public boolean onTrackballEvent(android.view.MotionEvent);
- method public abstract boolean onTune(android.net.Uri);
- method public void setOverlayViewEnabled(boolean);
- }
-
- public class TvView extends android.view.SurfaceView {
- ctor public TvView(android.content.Context);
- ctor public TvView(android.content.Context, android.util.AttributeSet);
- ctor public TvView(android.content.Context, android.util.AttributeSet, int);
- method public void bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCallback);
- method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
- method public boolean onUnhandledInputEvent(android.view.InputEvent);
- method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener);
- method public void unbindTvInput();
- }
-
- public static abstract interface TvView.OnUnhandledInputEventListener {
- method public abstract boolean onUnhandledInputEvent(android.view.InputEvent);
- }
-
-}
-
package android.util {
public class AndroidException extends java.lang.Exception {
@@ -31108,12 +30907,26 @@
method public static android.util.Range<T> create(T, T);
method public T getLower();
method public T getUpper();
+ method public boolean inRange(T);
}
- public final class Rational {
+ public final class Rational extends java.lang.Number implements java.lang.Comparable {
ctor public Rational(int, int);
+ method public int compareTo(android.util.Rational);
+ method public double doubleValue();
+ method public float floatValue();
method public int getDenominator();
method public int getNumerator();
+ method public int intValue();
+ method public boolean isFinite();
+ method public boolean isInfinite();
+ method public boolean isNaN();
+ method public boolean isZero();
+ method public long longValue();
+ field public static final android.util.Rational NEGATIVE_INFINITY;
+ field public static final android.util.Rational NaN;
+ field public static final android.util.Rational POSITIVE_INFINITY;
+ field public static final android.util.Rational ZERO;
}
public final class Size {
@@ -32618,7 +32431,6 @@
method protected int computeVerticalScrollRange();
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
- method public final android.animation.ValueAnimator createRevealAnimator(int, int, float, float);
method public void destroyDrawingCache();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
@@ -33267,6 +33079,10 @@
method public abstract boolean onTouch(android.view.View, android.view.MotionEvent);
}
+ public class ViewAnimationUtils {
+ method public static final android.animation.ValueAnimator createCircularReveal(android.view.View, int, int, float, float);
+ }
+
public class ViewConfiguration {
ctor public deprecated ViewConfiguration();
method public static android.view.ViewConfiguration get(android.content.Context);
@@ -33728,6 +33544,7 @@
method public android.transition.Transition getSharedElementEnterTransition();
method public android.transition.Transition getSharedElementExitTransition();
method public abstract int getStatusBarColor();
+ method public long getTransitionBackgroundFadeDuration();
method public android.transition.TransitionManager getTransitionManager();
method public abstract int getVolumeControlStream();
method public android.view.WindowManager getWindowManager();
@@ -33786,6 +33603,7 @@
method public abstract void setStatusBarColor(int);
method public abstract void setTitle(java.lang.CharSequence);
method public abstract deprecated void setTitleColor(int);
+ method public void setTransitionBackgroundFadeDuration(long);
method public void setTransitionManager(android.transition.TransitionManager);
method public void setType(int);
method public void setUiOptions(int);
@@ -34426,11 +34244,17 @@
public static final class CaptioningManager.CaptionStyle {
method public android.graphics.Typeface getTypeface();
+ method public boolean hasBackgroundColor();
+ method public boolean hasEdgeColor();
+ method public boolean hasEdgeType();
+ method public boolean hasForegroundColor();
+ method public boolean hasWindowColor();
field public static final int EDGE_TYPE_DEPRESSED = 4; // 0x4
field public static final int EDGE_TYPE_DROP_SHADOW = 2; // 0x2
field public static final int EDGE_TYPE_NONE = 0; // 0x0
field public static final int EDGE_TYPE_OUTLINE = 1; // 0x1
field public static final int EDGE_TYPE_RAISED = 3; // 0x3
+ field public static final int EDGE_TYPE_UNSPECIFIED = -1; // 0xffffffff
field public final int backgroundColor;
field public final int edgeColor;
field public final int edgeType;
@@ -34794,15 +34618,15 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
- public static final class CursorAnchorInfo.CursorAnchorInfoBuilder {
- ctor public CursorAnchorInfo.CursorAnchorInfoBuilder();
- method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder addCharacterRect(int, float, float, float, float);
+ public static final class CursorAnchorInfo.Builder {
+ ctor public CursorAnchorInfo.Builder();
+ method public android.view.inputmethod.CursorAnchorInfo.Builder addCharacterRect(int, float, float, float, float);
method public android.view.inputmethod.CursorAnchorInfo build();
method public void reset();
- method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setComposingText(int, java.lang.CharSequence);
- method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setInsertionMarkerLocation(float, float, float, float);
- method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setMatrix(android.graphics.Matrix);
- method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setSelectionRange(int, int);
+ method public android.view.inputmethod.CursorAnchorInfo.Builder setComposingText(int, java.lang.CharSequence);
+ method public android.view.inputmethod.CursorAnchorInfo.Builder setInsertionMarkerLocation(float, float, float, float);
+ method public android.view.inputmethod.CursorAnchorInfo.Builder setMatrix(android.graphics.Matrix);
+ method public android.view.inputmethod.CursorAnchorInfo.Builder setSelectionRange(int, int);
}
public class EditorInfo implements android.text.InputType android.os.Parcelable {
diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
index dce0a75..e6847a9 100644
--- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
+++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
@@ -20,6 +20,7 @@
import android.app.IActivityManager;
import android.app.IActivityManager.ContentProviderHolder;
import android.content.IContentProvider;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -33,7 +34,8 @@
enum CommandVerb {
UNSPECIFIED,
GET,
- PUT
+ PUT,
+ DELETE
}
static String[] mArgs;
@@ -74,6 +76,8 @@
mVerb = CommandVerb.GET;
} else if ("put".equalsIgnoreCase(arg)) {
mVerb = CommandVerb.PUT;
+ } else if ("delete".equalsIgnoreCase(arg)) {
+ mVerb = CommandVerb.DELETE;
} else {
// invalid
System.err.println("Invalid command: " + arg);
@@ -87,7 +91,7 @@
break; // invalid
}
mTable = arg.toLowerCase();
- } else if (mVerb == CommandVerb.GET) {
+ } else if (mVerb == CommandVerb.GET || mVerb == CommandVerb.DELETE) {
mKey = arg;
if (mNextArg >= mArgs.length) {
valid = true;
@@ -136,6 +140,10 @@
case PUT:
putForUser(provider, mUser, mTable, mKey, mValue);
break;
+ case DELETE:
+ System.out.println("Deleted "
+ + deleteForUser(provider, mUser, mTable, mKey) + " rows");
+ break;
default:
System.err.println("Unspecified command");
break;
@@ -211,9 +219,31 @@
}
}
+ int deleteForUser(IContentProvider provider, int userHandle,
+ final String table, final String key) {
+ Uri targetUri;
+ if ("system".equals(table)) targetUri = Settings.System.getUriFor(key);
+ else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key);
+ else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key);
+ else {
+ System.err.println("Invalid table; no delete performed");
+ throw new IllegalArgumentException("Invalid table " + table);
+ }
+
+ int num = 0;
+ try {
+ num = provider.delete(null, targetUri, null, null);
+ } catch (RemoteException e) {
+ System.err.println("Can't clear key " + key + " in " + table + " for user "
+ + userHandle);
+ }
+ return num;
+ }
+
private static void printUsage() {
System.err.println("usage: settings [--user NUM] get namespace key");
System.err.println(" settings [--user NUM] put namespace key value");
+ System.err.println(" settings [--user NUM] delete namespace key");
System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive");
System.err.println("If '--user NUM' is not given, the operations are performed on the owner user.");
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 2620c44..cbc8150 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -462,7 +462,9 @@
* anything behind it, then only the modal window will be reported
* (assuming it is the top one). For convenience the returned windows
* are ordered in a descending layer order, which is the windows that
- * are higher in the Z-order are reported first.
+ * are higher in the Z-order are reported first. Since the user can always
+ * interact with the window that has input focus by typing, the focused
+ * window is always returned (even if covered by a modal window).
* <p>
* <strong>Note:</strong> In order to access the windows your service has
* to declare the capability to retrieve window content by setting the
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 4f9ba59..4edb0c6 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -286,8 +286,8 @@
/**
* This flag indicates to the system that the accessibility service wants
* to access content of all interactive windows. An interactive window is a
- * window that can be touched by a sighted user when explore by touch is not
- * enabled. If this flag is not set your service will not receive
+ * window that has input focus or can be touched by a sighted user when explore
+ * by touch is not enabled. If this flag is not set your service will not receive
* {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED}
* events, calling AccessibilityService{@link AccessibilityService#getWindows()
* AccessibilityService.getWindows()} will return an empty list, and {@link
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 12fcdcf..806a55b 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -359,7 +359,29 @@
*/
public AuthenticatorDescription[] getAuthenticatorTypes() {
try {
- return mService.getAuthenticatorTypes();
+ return mService.getAuthenticatorTypes(UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ // will never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @hide
+ * Lists the currently registered authenticators for a given user id.
+ *
+ * <p>It is safe to call this method from the main thread.
+ *
+ * <p>The caller has to be in the same user or have the permission
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+ *
+ * @return An array of {@link AuthenticatorDescription} for every
+ * authenticator known to the AccountManager service. Empty (never
+ * null) if no authenticators are known.
+ */
+ public AuthenticatorDescription[] getAuthenticatorTypesAsUser(int userId) {
+ try {
+ return mService.getAuthenticatorTypes(userId);
} catch (RemoteException e) {
// will never happen
throw new RuntimeException(e);
@@ -389,6 +411,28 @@
/**
* @hide
+ * Lists all accounts of any type registered on the device for a given
+ * user id. Equivalent to getAccountsByType(null).
+ *
+ * <p>It is safe to call this method from the main thread.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#GET_ACCOUNTS}.
+ *
+ * @return An array of {@link Account}, one for each account. Empty
+ * (never null) if no accounts have been added.
+ */
+ public Account[] getAccountsAsUser(int userId) {
+ try {
+ return mService.getAccountsAsUser(null, userId);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @hide
* For use by internal activities. Returns the list of accounts that the calling package
* is authorized to use, particularly for shared accounts.
* @param packageName package name of the calling app.
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 86e279f..1373dc8 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -29,7 +29,7 @@
interface IAccountManager {
String getPassword(in Account account);
String getUserData(in Account account, String key);
- AuthenticatorDescription[] getAuthenticatorTypes();
+ AuthenticatorDescription[] getAuthenticatorTypes(int userId);
Account[] getAccounts(String accountType);
Account[] getAccountsForPackage(String packageName, int uid);
Account[] getAccountsByTypeForPackage(String type, String packageName);
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 933135d..06f5aca 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.content.res.Resources.NotFoundException;
@@ -25,6 +26,9 @@
import android.util.TypedValue;
import android.util.Xml;
import android.view.animation.AnimationUtils;
+
+import com.android.internal.R;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -66,11 +70,26 @@
*/
public static Animator loadAnimator(Context context, int id)
throws NotFoundException {
+ return loadAnimator(context.getResources(), context.getTheme(), id);
+ }
+
+ /**
+ * Loads an {@link Animator} object from a resource
+ *
+ * @param resources The resources
+ * @param theme The theme
+ * @param id The resource id of the animation to load
+ * @return The animator object reference by the specified id
+ * @throws android.content.res.Resources.NotFoundException when the animation cannot be loaded
+ * @hide
+ */
+ public static Animator loadAnimator(Resources resources, Theme theme, int id)
+ throws NotFoundException {
XmlResourceParser parser = null;
try {
- parser = context.getResources().getAnimation(id);
- return createAnimatorFromXml(context, parser);
+ parser = resources.getAnimation(id);
+ return createAnimatorFromXml(resources, theme, parser);
} catch (XmlPullParserException ex) {
Resources.NotFoundException rnf =
new Resources.NotFoundException("Can't load animation resource ID #0x" +
@@ -150,7 +169,8 @@
}
if (animator == null) {
- animator = createAnimatorFromXml(context, parser);
+ animator = createAnimatorFromXml(context.getResources(),
+ context.getTheme(), parser);
}
if (animator == null) {
@@ -166,103 +186,8 @@
}
}
- private static Animator createAnimatorFromXml(Context c, XmlPullParser parser)
- throws XmlPullParserException, IOException {
- return createAnimatorFromXml(c, parser, Xml.asAttributeSet(parser), null, 0);
- }
- private static Animator createAnimatorFromXml(Context c, XmlPullParser parser,
- AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
- throws XmlPullParserException, IOException {
-
- Animator anim = null;
- ArrayList<Animator> childAnims = null;
-
- // Make sure we are on a start tag.
- int type;
- int depth = parser.getDepth();
-
- while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
- && type != XmlPullParser.END_DOCUMENT) {
-
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- String name = parser.getName();
-
- if (name.equals("objectAnimator")) {
- anim = loadObjectAnimator(c, attrs);
- } else if (name.equals("animator")) {
- anim = loadAnimator(c, attrs, null);
- } else if (name.equals("set")) {
- anim = new AnimatorSet();
- TypedArray a = c.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.AnimatorSet);
- int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
- TOGETHER);
- createAnimatorFromXml(c, parser, attrs, (AnimatorSet) anim, ordering);
- a.recycle();
- } else {
- throw new RuntimeException("Unknown animator name: " + parser.getName());
- }
-
- if (parent != null) {
- if (childAnims == null) {
- childAnims = new ArrayList<Animator>();
- }
- childAnims.add(anim);
- }
- }
- if (parent != null && childAnims != null) {
- Animator[] animsArray = new Animator[childAnims.size()];
- int index = 0;
- for (Animator a : childAnims) {
- animsArray[index++] = a;
- }
- if (sequenceOrdering == TOGETHER) {
- parent.playTogether(animsArray);
- } else {
- parent.playSequentially(animsArray);
- }
- }
-
- return anim;
-
- }
-
- private static ObjectAnimator loadObjectAnimator(Context context, AttributeSet attrs)
- throws NotFoundException {
-
- ObjectAnimator anim = new ObjectAnimator();
-
- loadAnimator(context, attrs, anim);
-
- TypedArray a =
- context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.PropertyAnimator);
-
- String propertyName = a.getString(com.android.internal.R.styleable.PropertyAnimator_propertyName);
-
- anim.setPropertyName(propertyName);
-
- a.recycle();
-
- return anim;
- }
-
- /**
- * Creates a new animation whose parameters come from the specified context and
- * attributes set.
- *
- * @param context the application environment
- * @param attrs the set of attributes holding the animation parameters
- */
- private static ValueAnimator loadAnimator(Context context, AttributeSet attrs, ValueAnimator anim)
- throws NotFoundException {
-
- TypedArray a =
- context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animator);
-
+ private static void parseAnimatorFromTypeArray(ValueAnimator anim, TypedArray a) {
long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 300);
long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
@@ -378,11 +303,123 @@
if (evaluator != null) {
anim.setEvaluator(evaluator);
}
+ }
+
+ private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ return createAnimatorFromXml(res, theme, parser, Xml.asAttributeSet(parser), null, 0);
+ }
+
+ private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser,
+ AttributeSet attrs, AnimatorSet parent, int sequenceOrdering)
+ throws XmlPullParserException, IOException {
+
+ Animator anim = null;
+ ArrayList<Animator> childAnims = null;
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+
+ if (name.equals("objectAnimator")) {
+ anim = loadObjectAnimator(res, theme, attrs);
+ } else if (name.equals("animator")) {
+ anim = loadAnimator(res, theme, attrs, null);
+ } else if (name.equals("set")) {
+ anim = new AnimatorSet();
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimatorSet, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, com.android.internal.R.styleable.AnimatorSet);
+ }
+ int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
+ TOGETHER);
+ createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering);
+ a.recycle();
+ } else {
+ throw new RuntimeException("Unknown animator name: " + parser.getName());
+ }
+
+ if (parent != null) {
+ if (childAnims == null) {
+ childAnims = new ArrayList<Animator>();
+ }
+ childAnims.add(anim);
+ }
+ }
+ if (parent != null && childAnims != null) {
+ Animator[] animsArray = new Animator[childAnims.size()];
+ int index = 0;
+ for (Animator a : childAnims) {
+ animsArray[index++] = a;
+ }
+ if (sequenceOrdering == TOGETHER) {
+ parent.playTogether(animsArray);
+ } else {
+ parent.playSequentially(animsArray);
+ }
+ }
+
+ return anim;
+
+ }
+
+ private static ObjectAnimator loadObjectAnimator(Resources res, Theme theme, AttributeSet attrs)
+ throws NotFoundException {
+ ObjectAnimator anim = new ObjectAnimator();
+
+ loadAnimator(res, theme, attrs, anim);
+
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.PropertyAnimator, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
+ }
+
+ String propertyName = a.getString(R.styleable.PropertyAnimator_propertyName);
+
+ anim.setPropertyName(propertyName);
+
+ a.recycle();
+
+ return anim;
+ }
+
+ /**
+ * Creates a new animation whose parameters come from the specified context
+ * and attributes set.
+ *
+ * @param res The resources
+ * @param attrs The set of attributes holding the animation parameters
+ */
+ private static ValueAnimator loadAnimator(Resources res, Theme theme,
+ AttributeSet attrs, ValueAnimator anim)
+ throws NotFoundException {
+
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, R.styleable.Animator);
+ }
+
+ parseAnimatorFromTypeArray(anim, a);
final int resID =
a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
if (resID > 0) {
- anim.setInterpolator(AnimationUtils.loadInterpolator(context, resID));
+ anim.setInterpolator(AnimationUtils.loadInterpolator(res, theme, resID));
}
a.recycle();
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 130754e..8947550 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -16,11 +16,14 @@
package android.animation;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.Log;
import android.util.Property;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -41,10 +44,15 @@
*
*/
public final class ObjectAnimator extends ValueAnimator {
+ private static final String LOG_TAG = "ObjectAnimator";
+
private static final boolean DBG = false;
- // The target object on which the property exists, set in the constructor
- private Object mTarget;
+ /**
+ * A weak reference to the target object on which the property exists, set
+ * in the constructor. We'll cancel the animation if this goes away.
+ */
+ private WeakReference<Object> mTarget;
private String mPropertyName;
@@ -78,7 +86,7 @@
*
* @param propertyName The name of the property being animated. Should not be null.
*/
- public void setPropertyName(String propertyName) {
+ public void setPropertyName(@NonNull String propertyName) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
@@ -100,7 +108,7 @@
*
* @param property The property being animated. Should not be null.
*/
- public void setProperty(Property property) {
+ public void setProperty(@NonNull Property property) {
// mValues could be null if this is being constructed piecemeal. Just record the
// propertyName to be used later when setValues() is called if so.
if (mValues != null) {
@@ -134,6 +142,7 @@
* object (if there was just one) or a comma-separated list of all of the
* names (if there are more than one).</p>
*/
+ @Nullable
public String getPropertyName() {
String propertyName = null;
if (mPropertyName != null) {
@@ -176,7 +185,7 @@
* @param propertyName The name of the property being animated.
*/
private ObjectAnimator(Object target, String propertyName) {
- mTarget = target;
+ setTarget(target);
setPropertyName(propertyName);
}
@@ -187,7 +196,7 @@
* @param property The property being animated.
*/
private <T> ObjectAnimator(T target, Property<T, ?> property) {
- mTarget = target;
+ setTarget(target);
setProperty(property);
}
@@ -574,8 +583,9 @@
* @param path The <code>Path</code> to animate values along.
* @return An ObjectAnimator object that is set up to animate along <code>path</code>.
*/
+ @NonNull
public static ObjectAnimator ofObject(Object target, String propertyName,
- TypeConverter<PointF, ?> converter, Path path) {
+ @Nullable TypeConverter<PointF, ?> converter, Path path) {
PropertyValuesHolder pvh = PropertyValuesHolder.ofObject(propertyName, converter, path);
return ofPropertyValuesHolder(target, pvh);
}
@@ -595,6 +605,7 @@
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
+ @NonNull
public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
TypeEvaluator<V> evaluator, V... values) {
ObjectAnimator anim = new ObjectAnimator(target, property);
@@ -622,6 +633,7 @@
* @param values A set of values that the animation will animate between over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
+ @NonNull
public static <T, V, P> ObjectAnimator ofObject(T target, Property<T, P> property,
TypeConverter<V, P> converter, TypeEvaluator<V> evaluator, V... values) {
PropertyValuesHolder pvh = PropertyValuesHolder.ofObject(property, converter, evaluator,
@@ -644,8 +656,9 @@
* @param path The <code>Path</code> to animate values along.
* @return An ObjectAnimator object that is set up to animate along <code>path</code>.
*/
- public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
- TypeConverter<PointF, V> converter, Path path) {
+ @NonNull
+ public static <T, V> ObjectAnimator ofObject(T target, @NonNull Property<T, V> property,
+ @Nullable TypeConverter<PointF, V> converter, Path path) {
PropertyValuesHolder pvh = PropertyValuesHolder.ofObject(property, converter, path);
return ofPropertyValuesHolder(target, pvh);
}
@@ -667,10 +680,11 @@
* over time.
* @return An ObjectAnimator object that is set up to animate between the given values.
*/
+ @NonNull
public static ObjectAnimator ofPropertyValuesHolder(Object target,
PropertyValuesHolder... values) {
ObjectAnimator anim = new ObjectAnimator();
- anim.mTarget = target;
+ anim.setTarget(target);
anim.setValues(values);
return anim;
}
@@ -736,10 +750,10 @@
mAutoCancel = cancel;
}
- private boolean hasSameTargetAndProperties(Animator anim) {
+ private boolean hasSameTargetAndProperties(@Nullable Animator anim) {
if (anim instanceof ObjectAnimator) {
PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues();
- if (((ObjectAnimator) anim).getTarget() == mTarget &&
+ if (((ObjectAnimator) anim).getTarget() == getTarget() &&
mValues.length == theirValues.length) {
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvhMine = mValues[i];
@@ -789,11 +803,11 @@
}
}
if (DBG) {
- Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
+ Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
- Log.d("ObjectAnimator", " Values[" + i + "]: " +
+ Log.d(LOG_TAG, " Values[" + i + "]: " +
pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
}
@@ -818,9 +832,12 @@
if (!mInitialized) {
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
- int numValues = mValues.length;
- for (int i = 0; i < numValues; ++i) {
- mValues[i].setupSetterAndGetter(mTarget);
+ final Object target = getTarget();
+ if (target != null) {
+ final int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].setupSetterAndGetter(target);
+ }
}
super.initAnimation();
}
@@ -836,6 +853,7 @@
* <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>.
*/
@Override
+ @NonNull
public ObjectAnimator setDuration(long duration) {
super.setDuration(duration);
return this;
@@ -847,8 +865,9 @@
*
* @return The object being animated
*/
+ @Nullable
public Object getTarget() {
- return mTarget;
+ return mTarget == null ? null : mTarget.get();
}
/**
@@ -857,10 +876,10 @@
* @param target The object being animated
*/
@Override
- public void setTarget(Object target) {
- if (mTarget != target) {
- final Object oldTarget = mTarget;
- mTarget = target;
+ public void setTarget(@Nullable Object target) {
+ final Object oldTarget = getTarget();
+ if (oldTarget != target) {
+ mTarget = target == null ? null : new WeakReference<Object>(target);
if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) {
return;
}
@@ -872,18 +891,26 @@
@Override
public void setupStartValues() {
initAnimation();
- int numValues = mValues.length;
- for (int i = 0; i < numValues; ++i) {
- mValues[i].setupStartValue(mTarget);
+
+ final Object target = getTarget();
+ if (target != null) {
+ final int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].setupStartValue(target);
+ }
}
}
@Override
public void setupEndValues() {
initAnimation();
- int numValues = mValues.length;
- for (int i = 0; i < numValues; ++i) {
- mValues[i].setupEndValue(mTarget);
+
+ final Object target = getTarget();
+ if (target != null) {
+ final int numValues = mValues.length;
+ for (int i = 0; i < numValues; ++i) {
+ mValues[i].setupEndValue(target);
+ }
}
}
@@ -901,10 +928,17 @@
*/
@Override
void animateValue(float fraction) {
+ final Object target = getTarget();
+ if (mTarget != null && target == null) {
+ // We lost the target reference, cancel and clean up.
+ cancel();
+ return;
+ }
+
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
- mValues[i].setAnimatedValue(mTarget);
+ mValues[i].setAnimatedValue(target);
}
}
@@ -915,9 +949,10 @@
}
@Override
+ @NonNull
public String toString() {
String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " +
- mTarget;
+ getTarget();
if (mValues != null) {
for (int i = 0; i < mValues.length; ++i) {
returnVal += "\n " + mValues[i].toString();
diff --git a/core/java/android/annotation/PrivateApi.java b/core/java/android/annotation/PrivateApi.java
deleted file mode 100644
index 985eafe..0000000
--- a/core/java/android/annotation/PrivateApi.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.annotation;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Indicates an API is exposed for use by bundled applications.
- * <p>
- * These APIs are not guaranteed to remain consistent release-to-release,
- * and are not for use by apps linking against the SDK.
- * @hide
- */
-@Retention(RetentionPolicy.SOURCE)
-public @interface PrivateApi {
-}
diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java
new file mode 100644
index 0000000..55028eb
--- /dev/null
+++ b/core/java/android/annotation/SystemApi.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.annotation;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PACKAGE;
+import static java.lang.annotation.ElementType.TYPE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates an API is exposed for use by bundled system applications.
+ * <p>
+ * These APIs are not guaranteed to remain consistent release-to-release,
+ * and are not for use by apps linking against the Android SDK.
+ * </p><p>
+ * This annotation should only appear on API that is already marked <pre>@hide</pre>.
+ * </p>
+ *
+ * @hide
+ */
+@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface SystemApi {
+}
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index d4c4318..628875f 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -68,7 +68,7 @@
* select items accessible directly from the action bar as "action items". You can also
* modify various characteristics of the action bar or remove it completely.</p>
*
- * <p>When using the Quantum themes (default in API 21 or newer) the navigation button
+ * <p>When using the Material themes (default in API 21 or newer) the navigation button
* (formerly "Home") takes over the space previously occupied by the application icon.
* Apps wishing to express a stronger branding should use their brand colors heavily
* in the action bar and other application chrome or use a {@link #setLogo(int) logo}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 23b5f29..f6883e2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5326,7 +5326,8 @@
*
* @hide
*/
- void convertToTranslucent(TranslucentConversionListener callback, ActivityOptions options) {
+ public void convertToTranslucent(TranslucentConversionListener callback,
+ ActivityOptions options) {
boolean drawComplete;
try {
mTranslucentCallback = callback;
@@ -5615,6 +5616,34 @@
mExitTransitionListener = listener;
}
+ /**
+ * Postpone the entering activity transition when Activity was started with
+ * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
+ * android.util.Pair[])}.
+ * <p>This method gives the Activity the ability to delay starting the entering and
+ * shared element transitions until all data is loaded. Until then, the Activity won't
+ * draw into its window, leaving the window transparent. This may also cause the
+ * returning animation to be delayed until data is ready. This method should be
+ * called in {@link #onCreate(android.os.Bundle)} or in
+ * {@link #onActivityReenter(int, android.content.Intent)}.
+ * {@link #startPostponedEnterTransition()} must be called to allow the Activity to
+ * start the transitions. If the Activity did not use
+ * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
+ * android.util.Pair[])}, then this method does nothing.</p>
+ */
+ public void postponeEnterTransition() {
+ mActivityTransitionState.postponeEnterTransition();
+ }
+
+ /**
+ * Begin postponed transitions after {@link #postponeEnterTransition()} was called.
+ * If postponeEnterTransition() was called, you must call startPostponedEnterTransition()
+ * to have your Activity start drawing.
+ */
+ public void startPostponedEnterTransition() {
+ mActivityTransitionState.startPostponedEnterTransition();
+ }
+
// ------------------ Internal API ------------------
final void setParent(Activity parent) {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index a057c3e..9160452 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -394,8 +394,18 @@
if (sharedElements != null) {
for (int i = 0; i < sharedElements.length; i++) {
Pair<View, String> sharedElement = sharedElements[i];
- names.add(sharedElement.second);
- mappedNames.add(sharedElement.first.getViewName());
+ String sharedElementName = sharedElement.second;
+ if (sharedElementName == null) {
+ throw new IllegalArgumentException("Shared element name must not be null");
+ }
+ String viewName = sharedElement.first.getViewName();
+ if (viewName == null) {
+ throw new IllegalArgumentException("Shared elements must have non-null " +
+ "viewNames");
+ }
+
+ names.add(sharedElementName);
+ mappedNames.add(viewName);
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d9adba3..ea46044 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -98,6 +98,7 @@
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.util.FastPrintWriter;
import com.android.org.conscrypt.OpenSSLSocketImpl;
+import com.android.org.conscrypt.TrustedCertificateStore;
import com.google.android.collect.Lists;
import dalvik.system.VMRuntime;
@@ -5049,6 +5050,10 @@
Security.addProvider(new AndroidKeyStoreProvider());
+ // Make sure TrustedCertificateStore looks in the right place for CA certificates
+ final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
+ TrustedCertificateStore.setDefaultUserDirectory(configDir);
+
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 5e4ddd0..0cccedc 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -129,9 +129,6 @@
protected static final String KEY_SCALE_TYPE = "shared_element:scaleType";
protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
- // The background fade in/out duration. 150ms is pretty quick, but not abrupt.
- public static final int FADE_BACKGROUND_DURATION_MS = 150;
-
protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
/**
@@ -195,6 +192,11 @@
*/
public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
+ /**
+ * Send the shared element positions.
+ */
+ public static final int MSG_SEND_SHARED_ELEMENT_DESTINATION = 108;
+
final private Window mWindow;
final protected ArrayList<String> mAllSharedElementNames;
final protected ArrayList<View> mSharedElements = new ArrayList<View>();
@@ -209,11 +211,21 @@
ArrayList<String> allSharedElementNames,
ArrayList<String> accepted, ArrayList<String> localNames,
SharedElementListener listener, boolean isReturning) {
+ this(window, allSharedElementNames, listener, isReturning);
+ viewsReady(accepted, localNames);
+ }
+
+ public ActivityTransitionCoordinator(Window window,
+ ArrayList<String> allSharedElementNames,
+ SharedElementListener listener, boolean isReturning) {
super(new Handler());
mWindow = window;
mListener = listener;
mAllSharedElementNames = allSharedElementNames;
mIsReturning = isReturning;
+ }
+
+ protected void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
setSharedElements(accepted, localNames);
if (getViewsTransition() != null) {
getDecor().captureTransitioningViews(mTransitioningViews);
@@ -246,13 +258,8 @@
if (view == null) {
mEpicenterCallback.setEpicenter(null);
} else {
- int[] loc = new int[2];
- view.getLocationOnScreen(loc);
- int left = loc[0] + Math.round(view.getTranslationX());
- int top = loc[1] + Math.round(view.getTranslationY());
- int right = left + view.getWidth();
- int bottom = top + view.getHeight();
- Rect epicenter = new Rect(left, top, right, bottom);
+ Rect epicenter = new Rect();
+ view.getBoundsOnScreen(epicenter);
mEpicenterCallback.setEpicenter(epicenter);
}
}
@@ -269,6 +276,8 @@
return names;
}
+ public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
+
public static void setViewVisibility(Collection<View> views, int visibility) {
if (views != null) {
for (View view : views) {
@@ -335,6 +344,10 @@
String name = mAllSharedElementNames.get(i);
View sharedElement = sharedElements.get(name);
if (sharedElement != null) {
+ if (sharedElement.getViewName() == null) {
+ throw new IllegalArgumentException("Shared elements must have " +
+ "non-null viewNames");
+ }
mSharedElementNames.add(name);
mSharedElements.add(sharedElement);
}
@@ -487,15 +500,19 @@
protected Bundle captureSharedElementState() {
Bundle bundle = new Bundle();
- int[] tempLoc = new int[2];
+ Rect tempBounds = new Rect();
for (int i = 0; i < mSharedElementNames.size(); i++) {
View sharedElement = mSharedElements.get(i);
String name = mSharedElementNames.get(i);
- captureSharedElementState(sharedElement, name, bundle, tempLoc);
+ captureSharedElementState(sharedElement, name, bundle, tempBounds);
}
return bundle;
}
+ protected long getFadeDuration() {
+ return getWindow().getTransitionBackgroundFadeDuration();
+ }
+
/**
* Captures placement information for Views with a shared element name for
* Activity Transitions.
@@ -504,20 +521,19 @@
* @param name The shared element name in the target Activity to apply the placement
* information for.
* @param transitionArgs Bundle to store shared element placement information.
- * @param tempLoc A temporary int[2] for capturing the current location of views.
+ * @param tempBounds A temporary Rect for capturing the current location of views.
*/
private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
- int[] tempLoc) {
+ Rect tempBounds) {
Bundle sharedElementBundle = new Bundle();
- view.getLocationOnScreen(tempLoc);
- float scaleX = view.getScaleX();
- sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
- int width = Math.round(view.getWidth() * scaleX);
+ tempBounds.set(0, 0, view.getWidth(), view.getHeight());
+ view.getBoundsOnScreen(tempBounds);
+ sharedElementBundle.putInt(KEY_SCREEN_X, tempBounds.left);
+ int width = tempBounds.width();
sharedElementBundle.putInt(KEY_WIDTH, width);
- float scaleY = view.getScaleY();
- sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
- int height = Math.round(view.getHeight() * scaleY);
+ sharedElementBundle.putInt(KEY_SCREEN_Y, tempBounds.top);
+ int height = tempBounds.height();
sharedElementBundle.putInt(KEY_HEIGHT, height);
sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
@@ -558,7 +574,7 @@
public void setEpicenter(Rect epicenter) { mEpicenter = epicenter; }
@Override
- public Rect getEpicenter(Transition transition) {
+ public Rect onGetEpicenter(Transition transition) {
return mEpicenter;
}
}
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index b32e9ad..d94dadd 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -87,6 +87,11 @@
*/
private boolean mHasExited;
+ /**
+ * Postpone painting and starting the enter transition until this is false.
+ */
+ private boolean mIsEnterPostponed;
+
public ActivityTransitionState() {
}
@@ -140,15 +145,38 @@
if (mEnterActivityOptions.isReturning()) {
restoreExitedViews();
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
- mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
- resultReceiver, sharedElementNames, mExitingFrom, mExitingTo);
+ }
+ mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
+ resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning());
+
+ if (!mIsEnterPostponed) {
+ startEnter();
+ }
+ }
+
+ public void postponeEnterTransition() {
+ mIsEnterPostponed = true;
+ }
+
+ public void startPostponedEnterTransition() {
+ if (mIsEnterPostponed) {
+ mIsEnterPostponed = false;
+ if (mEnterTransitionCoordinator != null) {
+ startEnter();
+ }
+ }
+ }
+
+ private void startEnter() {
+ if (mEnterActivityOptions.isReturning()) {
+ mEnterTransitionCoordinator.viewsReady(mExitingFrom, mExitingTo);
} else {
- mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
- resultReceiver, sharedElementNames, null, null);
- mEnteringNames = sharedElementNames;
+ mEnterTransitionCoordinator.viewsReady(null, null);
+ mEnteringNames = mEnterTransitionCoordinator.getAllSharedElementNames();
mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames();
mEnteringTo = mEnterTransitionCoordinator.getMappedNames();
}
+
mExitingFrom = null;
mExitingTo = null;
mEnterActivityOptions = null;
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 097c64e..94ea2c5 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -53,6 +53,7 @@
private int mHeight;
private Surface mSurface;
private int mLastVisibility;
+ private ActivityViewCallback mActivityViewCallback;
// Only one IIntentSender or Intent may be queued at a time. Most recent one wins.
IIntentSender mQueuedPendingIntent;
@@ -254,6 +255,25 @@
}
}
+ /**
+ * Set the callback to use to report certain state changes.
+ * @param callback The callback to report events to.
+ *
+ * @see ActivityViewCallback
+ */
+ public void setCallback(ActivityViewCallback callback) {
+ mActivityViewCallback = callback;
+ }
+
+ public static abstract class ActivityViewCallback {
+ /**
+ * Called when all activities in the ActivityView have completed and been removed. Register
+ * using {@link ActivityView#setCallback(ActivityViewCallback)}. Each ActivityView may
+ * have at most one callback registered.
+ */
+ public abstract void onAllActivitiesComplete(ActivityView view);
+ }
+
private class ActivityViewSurfaceTextureListener implements SurfaceTextureListener {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width,
@@ -313,14 +333,32 @@
if (DEBUG) Log.v(TAG, "setVisible(): container=" + container + " visible=" + visible +
" ActivityView=" + mActivityViewWeakReference.get());
}
+
+ @Override
+ public void onAllActivitiesComplete(IBinder container) {
+ final ActivityView activityView = mActivityViewWeakReference.get();
+ if (activityView != null) {
+ final ActivityViewCallback callback = activityView.mActivityViewCallback;
+ if (callback != null) {
+ activityView.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onAllActivitiesComplete(activityView);
+ }
+ });
+ }
+ }
+ }
}
private static class ActivityContainerWrapper {
private final IActivityContainer mIActivityContainer;
private final CloseGuard mGuard = CloseGuard.get();
+ boolean mOpened; // Protected by mGuard.
ActivityContainerWrapper(IActivityContainer container) {
mIActivityContainer = container;
+ mOpened = true;
mGuard.open("release");
}
@@ -388,11 +426,16 @@
}
void release() {
- if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called");
- try {
- mIActivityContainer.release();
- mGuard.close();
- } catch (RemoteException e) {
+ synchronized (mGuard) {
+ if (mOpened) {
+ if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called");
+ try {
+ mIActivityContainer.release();
+ mGuard.close();
+ } catch (RemoteException e) {
+ }
+ mOpened = false;
+ }
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4f335bb..52d4585 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -59,9 +59,7 @@
import android.hardware.ISerialManager;
import android.hardware.SerialManager;
import android.hardware.SystemSensorManager;
-import android.hardware.hdmi.HdmiCecManager;
import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.IHdmiCecService;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.camera2.CameraManager;
import android.hardware.display.DisplayManager;
@@ -75,6 +73,8 @@
import android.media.AudioManager;
import android.media.MediaRouter;
import android.media.session.MediaSessionManager;
+import android.media.tv.ITvInputManager;
+import android.media.tv.TvInputManager;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
import android.net.EthernetManager;
@@ -119,8 +119,6 @@
import android.service.fingerprint.FingerprintManagerReceiver;
import android.service.fingerprint.FingerprintService;
import android.telephony.TelephonyManager;
-import android.tv.ITvInputManager;
-import android.tv.TvInputManager;
import android.content.ClipboardManager;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
@@ -137,7 +135,7 @@
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
import android.app.admin.DevicePolicyManager;
-import android.app.task.ITaskManager;
+import android.app.job.IJobScheduler;
import android.app.trust.TrustManager;
import com.android.internal.annotations.GuardedBy;
@@ -386,12 +384,6 @@
return new BluetoothManager(ctx);
}});
- registerService(HDMI_CEC_SERVICE, new StaticServiceFetcher() {
- public Object createStaticService() {
- IBinder b = ServiceManager.getService(HDMI_CEC_SERVICE);
- return new HdmiCecManager(IHdmiCecService.Stub.asInterface(b));
- }});
-
registerService(HDMI_CONTROL_SERVICE, new StaticServiceFetcher() {
public Object createStaticService() {
IBinder b = ServiceManager.getService(HDMI_CONTROL_SERVICE);
@@ -714,10 +706,10 @@
return new UsageStatsManager(ctx.getOuterContext());
}});
- registerService(TASK_SERVICE, new ServiceFetcher() {
+ registerService(JOB_SCHEDULER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
- IBinder b = ServiceManager.getService(TASK_SERVICE);
- return new TaskManagerImpl(ITaskManager.Stub.asInterface(b));
+ IBinder b = ServiceManager.getService(JOB_SCHEDULER_SERVICE);
+ return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b));
}});
}
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index a8617b8..f54cb87 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -29,6 +29,7 @@
import android.util.ArrayMap;
import android.util.Pair;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
@@ -52,18 +53,38 @@
private boolean mIsCanceled;
private ObjectAnimator mBackgroundAnimator;
private boolean mIsExitTransitionComplete;
+ private boolean mIsReadyForTransition;
+ private Bundle mSharedElementsBundle;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
- ArrayList<String> sharedElementNames,
- ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
- super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
- getListener(activity, acceptedNames), acceptedNames != null);
+ ArrayList<String> sharedElementNames, boolean isReturning) {
+ super(activity.getWindow(), sharedElementNames,
+ getListener(activity, isReturning), isReturning);
mActivity = activity;
setResultReceiver(resultReceiver);
prepareEnter();
Bundle resultReceiverBundle = new Bundle();
resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
+ getDecor().getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ if (mIsReadyForTransition) {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ }
+ return mIsReadyForTransition;
+ }
+ });
+ }
+
+ public void viewsReady(ArrayList<String> accepted, ArrayList<String> localNames) {
+ if (mIsReadyForTransition) {
+ return;
+ }
+ super.viewsReady(accepted, localNames);
+
+ mIsReadyForTransition = true;
if (mIsReturning) {
mHandler = new Handler() {
@Override
@@ -72,14 +93,35 @@
}
};
mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
- Bundle state = captureSharedElementState();
- mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
+ send(MSG_SEND_SHARED_ELEMENT_DESTINATION, null);
+ }
+ setViewVisibility(mSharedElements, View.INVISIBLE);
+ if (getViewsTransition() != null) {
+ setViewVisibility(mTransitioningViews, View.INVISIBLE);
+ }
+ if (mSharedElementsBundle != null) {
+ onTakeSharedElements();
}
}
- private static SharedElementListener getListener(Activity activity,
- ArrayList<String> acceptedNames) {
- boolean isReturning = acceptedNames != null;
+ private void sendSharedElementDestination() {
+ ViewGroup decor = getDecor();
+ if (!decor.isLayoutRequested()) {
+ Bundle state = captureSharedElementState();
+ mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
+ } else {
+ getDecor().getViewTreeObserver()
+ .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ return true;
+ }
+ });
+ }
+ }
+
+ private static SharedElementListener getListener(Activity activity, boolean isReturning) {
return isReturning ? activity.mExitTransitionListener : activity.mEnterTransitionListener;
}
@@ -91,7 +133,8 @@
if (mHandler != null) {
mHandler.removeMessages(MSG_CANCEL);
}
- onTakeSharedElements(resultData);
+ mSharedElementsBundle = resultData;
+ onTakeSharedElements();
}
break;
case MSG_EXIT_TRANSITION_COMPLETE:
@@ -105,6 +148,9 @@
case MSG_CANCEL:
cancel();
break;
+ case MSG_SEND_SHARED_ELEMENT_DESTINATION:
+ sendSharedElementDestination();
+ break;
}
}
@@ -119,7 +165,7 @@
mSharedElementNames.clear();
mSharedElements.clear();
mAllSharedElementNames.clear();
- onTakeSharedElements(null);
+ startSharedElementTransition(null);
onRemoteExitTransitionComplete();
}
}
@@ -129,10 +175,6 @@
}
protected void prepareEnter() {
- setViewVisibility(mSharedElements, View.INVISIBLE);
- if (getViewsTransition() != null) {
- setViewVisibility(mTransitioningViews, View.INVISIBLE);
- }
mActivity.overridePendingTransition(0, 0);
if (!mIsReturning) {
mActivity.convertToTranslucent(null, null);
@@ -165,7 +207,25 @@
}
}
- protected void onTakeSharedElements(Bundle sharedElementState) {
+ protected void onTakeSharedElements() {
+ if (!mIsReadyForTransition || mSharedElementsBundle == null) {
+ return;
+ }
+ final Bundle sharedElementState = mSharedElementsBundle;
+ mSharedElementsBundle = null;
+ getDecor().getViewTreeObserver()
+ .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+ startSharedElementTransition(sharedElementState);
+ return false;
+ }
+ });
+ getDecor().invalidate();
+ }
+
+ private void startSharedElementTransition(Bundle sharedElementState) {
setEpicenter();
// Remove rejected shared elements
ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
@@ -256,7 +316,7 @@
if (background != null) {
background = background.mutate();
mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 255);
- mBackgroundAnimator.setDuration(FADE_BACKGROUND_DURATION_MS);
+ mBackgroundAnimator.setDuration(getFadeDuration());
mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -279,8 +339,8 @@
}
public void stop() {
+ makeOpaque();
mHasStopped = true;
- mActivity = null;
mIsCanceled = true;
mResultReceiver = null;
if (mBackgroundAnimator != null) {
@@ -290,7 +350,7 @@
}
private void makeOpaque() {
- if (!mHasStopped) {
+ if (!mHasStopped && mActivity != null) {
mActivity.convertFromTranslucent();
mActivity = null;
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index a71d649..8d5b8317 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -199,7 +199,7 @@
}
}
});
- mBackgroundAnimator.setDuration(FADE_BACKGROUND_DURATION_MS);
+ mBackgroundAnimator.setDuration(getFadeDuration());
mBackgroundAnimator.start();
}
}
@@ -294,7 +294,7 @@
}
private void finishIfNecessary() {
- if (mIsReturning && mExitNotified && (mSharedElements.isEmpty()
+ if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty()
|| mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
mActivity.finish();
mActivity.overridePendingTransition(0, 0);
diff --git a/core/java/android/app/IActivityContainerCallback.aidl b/core/java/android/app/IActivityContainerCallback.aidl
index 7f6d2c3..99d0a6f 100644
--- a/core/java/android/app/IActivityContainerCallback.aidl
+++ b/core/java/android/app/IActivityContainerCallback.aidl
@@ -21,4 +21,5 @@
/** @hide */
interface IActivityContainerCallback {
oneway void setVisible(IBinder container, boolean visible);
+ oneway void onAllActivitiesComplete(IBinder container);
}
diff --git a/core/java/android/app/JobSchedulerImpl.java b/core/java/android/app/JobSchedulerImpl.java
new file mode 100644
index 0000000..09038d5
--- /dev/null
+++ b/core/java/android/app/JobSchedulerImpl.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// in android.app so ContextImpl has package access
+package android.app;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.app.job.IJobScheduler;
+import android.os.RemoteException;
+
+import java.util.List;
+
+
+/**
+ * Concrete implementation of the JobScheduler interface
+ * @hide
+ */
+public class JobSchedulerImpl extends JobScheduler {
+ IJobScheduler mBinder;
+
+ /* package */ JobSchedulerImpl(IJobScheduler binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public int schedule(JobInfo job) {
+ try {
+ return mBinder.schedule(job);
+ } catch (RemoteException e) {
+ return JobScheduler.RESULT_FAILURE;
+ }
+ }
+
+ @Override
+ public void cancel(int jobId) {
+ try {
+ mBinder.cancel(jobId);
+ } catch (RemoteException e) {}
+
+ }
+
+ @Override
+ public void cancelAll() {
+ try {
+ mBinder.cancelAll();
+ } catch (RemoteException e) {}
+
+ }
+
+ @Override
+ public List<JobInfo> getAllPendingJobs() {
+ try {
+ return mBinder.getAllPendingJobs();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8dba1dc..276f936 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -17,21 +17,25 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaSessionToken;
import android.net.Uri;
import android.os.BadParcelableException;
-import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
@@ -70,6 +74,15 @@
private static final String TAG = "Notification";
/**
+ * An activity that provides a user interface for adjusting notification preferences for its
+ * containing application. Optional but recommended for apps that post
+ * {@link android.app.Notification Notifications}.
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String INTENT_CATEGORY_NOTIFICATION_PREFERENCES
+ = "android.intent.category.NOTIFICATION_PREFERENCES";
+
+ /**
* Use all default values (where applicable).
*/
public static final int DEFAULT_ALL = ~0;
@@ -2305,7 +2318,23 @@
return this;
}
+ private Bitmap getProfileBadge() {
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ Drawable badge = userManager.getBadgeForUser(android.os.Process.myUserHandle());
+ if (badge == null) {
+ return null;
+ }
+ final int width = badge.getIntrinsicWidth();
+ final int height = badge.getIntrinsicHeight();
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ badge.setBounds(0, 0, width, height);
+ badge.draw(canvas);
+ return bitmap;
+ }
+
private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) {
+ Bitmap profileIcon = getProfileBadge();
RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
boolean showLine3 = false;
boolean showLine2 = false;
@@ -2313,6 +2342,12 @@
if (mPriority < PRIORITY_LOW) {
// TODO: Low priority presentation
}
+ if (profileIcon != null) {
+ contentView.setImageViewBitmap(R.id.profile_icon, profileIcon);
+ contentView.setViewVisibility(R.id.profile_icon, View.VISIBLE);
+ } else {
+ contentView.setViewVisibility(R.id.profile_icon, View.GONE);
+ }
if (mLargeIcon != null) {
contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
processLargeIcon(mLargeIcon, contentView);
@@ -2473,7 +2508,7 @@
/**
* @return Whether we are currently building a notification from a legacy (an app that
- * doesn't create quantum notifications by itself) app.
+ * doesn't create material notifications by itself) app.
*/
private boolean isLegacy() {
return mColorUtil != null;
@@ -2531,7 +2566,7 @@
R.id.icon,
true,
-1,
- mColor,
+ resolveColor(),
PorterDuff.Mode.SRC_ATOP,
-1);
}
@@ -2558,17 +2593,22 @@
R.id.right_icon,
true,
-1,
- mColor,
+ resolveColor(),
PorterDuff.Mode.SRC_ATOP,
-1);
}
}
+ private int sanitizeColor() {
+ if (mColor != COLOR_DEFAULT) {
+ mColor |= 0xFF000000; // no alpha for custom colors
+ }
+ return mColor;
+ }
+
private int resolveColor() {
if (mColor == COLOR_DEFAULT) {
- mColor = mContext.getResources().getColor(R.color.notification_icon_bg_color);
- } else {
- mColor |= 0xFF000000; // no alpha for custom colors
+ return mContext.getResources().getColor(R.color.notification_icon_bg_color);
}
return mColor;
}
@@ -2584,7 +2624,7 @@
n.iconLevel = mSmallIconLevel;
n.number = mNumber;
- n.color = resolveColor();
+ n.color = sanitizeColor();
n.contentView = makeContentView();
n.contentIntent = mContentIntent;
@@ -2693,31 +2733,31 @@
private int getBaseLayoutResource() {
- return R.layout.notification_template_quantum_base;
+ return R.layout.notification_template_material_base;
}
private int getBigBaseLayoutResource() {
- return R.layout.notification_template_quantum_big_base;
+ return R.layout.notification_template_material_big_base;
}
private int getBigPictureLayoutResource() {
- return R.layout.notification_template_quantum_big_picture;
+ return R.layout.notification_template_material_big_picture;
}
private int getBigTextLayoutResource() {
- return R.layout.notification_template_quantum_big_text;
+ return R.layout.notification_template_material_big_text;
}
private int getInboxLayoutResource() {
- return R.layout.notification_template_quantum_inbox;
+ return R.layout.notification_template_material_inbox;
}
private int getActionLayoutResource() {
- return R.layout.notification_quantum_action;
+ return R.layout.notification_material_action;
}
private int getActionTombstoneLayoutResource() {
- return R.layout.notification_quantum_action_tombstone;
+ return R.layout.notification_material_action_tombstone;
}
}
@@ -3212,7 +3252,7 @@
private RemoteViews generateMediaActionButton(Action action) {
final boolean tombstone = (action.actionIntent == null);
RemoteViews button = new RemoteViews(mBuilder.mContext.getPackageName(),
- R.layout.notification_quantum_media_action);
+ R.layout.notification_material_media_action);
button.setImageViewResource(R.id.action0, action.icon);
if (!tombstone) {
button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
@@ -3223,7 +3263,7 @@
private RemoteViews makeMediaContentView() {
RemoteViews view = mBuilder.applyStandardTemplate(
- R.layout.notification_template_quantum_media, true /* 1U */);
+ R.layout.notification_template_material_media, true /* 1U */);
final int numActions = mBuilder.mActions.size();
final int N = mActionsToShowInCompact == null
@@ -3248,7 +3288,7 @@
private RemoteViews makeMediaBigContentView() {
RemoteViews big = mBuilder.applyStandardTemplate(
- R.layout.notification_template_quantum_big_media, false);
+ R.layout.notification_template_material_big_media, false);
final int N = Math.min(mBuilder.mActions.size(), MAX_MEDIA_BUTTONS);
if (N > 0) {
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 33c3409..261b15d 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -253,6 +253,7 @@
* for more information on these schemes.
*/
public final static String SUGGEST_COLUMN_ICON_1 = "suggest_icon_1";
+
/**
* Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column,
* then all suggestions will be provided in a format that includes space for two small icons,
@@ -269,6 +270,24 @@
* for more information on these schemes.
*/
public final static String SUGGEST_COLUMN_ICON_2 = "suggest_icon_2";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your cursor includes this column,
+ * then the image will be displayed when forming the suggestion. The suggested dimension for
+ * the image is 270x400 px for portrait mode and 400x225 px for landscape mode. The data in the
+ * column must be a resource ID of a drawable, or a URI in one of the following formats:
+ *
+ * <ul>
+ * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
+ * <li>file ({@link android.content.ContentResolver#SCHEME_FILE})</li>
+ * </ul>
+ *
+ * See {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)}
+ * for more information on these schemes.
+ */
+ public final static String SUGGEST_COLUMN_RESULT_CARD_IMAGE = "suggest_result_card_image";
+
/**
* Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i>
* this element exists at the given row, this is the action that will be used when
@@ -279,6 +298,7 @@
* it from the cursor.
*/
public final static String SUGGEST_COLUMN_INTENT_ACTION = "suggest_intent_action";
+
/**
* Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i>
* this element exists at the given row, this is the data that will be used when
@@ -289,6 +309,7 @@
* it is more efficient to specify it using XML metadata and omit it from the cursor.
*/
public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data";
+
/**
* Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i>
* this element exists at the given row, this is the data that will be used when
@@ -297,6 +318,7 @@
* an extra under the key {@link #EXTRA_DATA_KEY}.
*/
public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data";
+
/**
* Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i>
* this element exists at the given row, then "/" and this value will be appended to the data
@@ -304,6 +326,7 @@
* appropriate base string.
*/
public final static String SUGGEST_COLUMN_INTENT_DATA_ID = "suggest_intent_data_id";
+
/**
* Column name for suggestions cursor. <i>Required if action is
* {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}, optional otherwise.</i> If this
@@ -331,6 +354,89 @@
"suggest_spinner_while_refreshing";
/**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content is media type, you
+ * should provide this column so search app could understand more about your content. The data
+ * in the column must specify the MIME type of the content.
+ */
+ public final static String SUGGEST_COLUMN_CONTENT_TYPE = "suggest_content_type";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content is media type, you
+ * should provide this column to specify whether your content is live media such as live video
+ * or live audio. The value in the column is of integer type with value of either 0 indicating
+ * non-live content or 1 indicating live content.
+ */
+ public final static String SUGGEST_COLUMN_IS_LIVE = "suggest_is_live";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content is video, you should
+ * provide this column to specify the number of vertical lines. The data in the column is of
+ * integer type.
+ */
+ public final static String SUGGEST_COLUMN_VIDEO_WIDTH = "suggest_video_width";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content is video, you should
+ * provide this column to specify the number of horizontal lines. The data in the column is of
+ * integer type.
+ */
+ public final static String SUGGEST_COLUMN_VIDEO_HEIGHT = "suggest_video_height";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content contains audio, you
+ * should provide this column to specify the audio channel configuration. The data in the
+ * column is string with format like "channels.subchannels" such as "1.0" or "5.1".
+ */
+ public final static String SUGGEST_COLUMN_AUDIO_CHANNEL_CONFIG = "suggest_audio_channel_config";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content is purchasable, you
+ * should provide this column to specify the displayable string representation of the purchase
+ * price of your content including the currency and the amount. If it's free, you should
+ * provide localized string to specify that it's free. This column can be omitted if the content
+ * is not applicable to purchase.
+ */
+ public final static String SUGGEST_COLUMN_PURCHASE_PRICE = "suggest_purchase_price";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content is rentable, you
+ * should provide this column to specify the displayable string representation of the rental
+ * price of your content including the currency and the amount. If it's free, you should
+ * provide localized string to specify that it's free. This column can be ommitted if the
+ * content is not applicable to rent.
+ */
+ public final static String SUGGEST_COLUMN_RENTAL_PRICE = "suggest_rental_price";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content has a rating, you
+ * should provide this column to specify the rating style of your content. The data in the
+ * column must be one of the constant values specified in {@link android.media.Rating}
+ */
+ public final static String SUGGEST_COLUMN_RATING_STYLE = "suggest_rating_style";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content has a rating, you
+ * should provide this column to specify the rating score of your content. The data in the
+ * column is of float type. See {@link android.media.Rating} about valid rating scores for each
+ * rating style.
+ */
+ public final static String SUGGEST_COLUMN_RATING_SCORE = "suggest_rating_score";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content is video or audio and
+ * has a known production year, you should provide this column to specify the production year
+ * of your content. The data in the column is of integer type.
+ */
+ public final static String SUGGEST_COLUMN_PRODUCTION_YEAR = "suggest_production_year";
+
+ /**
+ * Column name for suggestions cursor. <i>Optional.</i> If your content is video or audio, you
+ * should provide this column to specify the duration of your content in milliseconds. The data
+ * in the column is of long type.
+ */
+ public final static String SUGGEST_COLUMN_DURATION = "suggest_duration";
+
+ /**
* Column name for suggestions cursor. <i>Optional.</i> This column is used to specify
* additional flags per item. Multiple flags can be specified.
* <p>
diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java
deleted file mode 100644
index f42839e..0000000
--- a/core/java/android/app/TaskManagerImpl.java
+++ /dev/null
@@ -1,62 +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.
- */
-
-// in android.app so ContextImpl has package access
-package android.app;
-
-import android.app.task.ITaskManager;
-import android.app.task.Task;
-import android.app.task.TaskManager;
-
-import java.util.List;
-
-
-/**
- * Concrete implementation of the TaskManager interface
- * @hide
- */
-public class TaskManagerImpl extends TaskManager {
- ITaskManager mBinder;
-
- /* package */ TaskManagerImpl(ITaskManager binder) {
- mBinder = binder;
- }
-
- @Override
- public int schedule(Task task) {
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
- public void cancel(int taskId) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void cancelAll() {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public List<Task> getAllPendingTasks() {
- // TODO Auto-generated method stub
- return null;
- }
-
-}
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index 85e970c..0d947217 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -293,7 +293,7 @@
IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
IVoiceInteractorCallback callback) throws RemoteException {
- return interactor.startConfirmation(packageName, callback, mCommand, mArgs);
+ return interactor.startCommand(packageName, callback, mCommand, mArgs);
}
}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index ee222a9..45a2625 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -16,6 +16,7 @@
package android.app.admin;
+import android.accounts.AccountManager;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
@@ -165,12 +166,14 @@
= "android.app.action.ACTION_PASSWORD_EXPIRING";
/**
- * Broadcast Action: This broadcast is sent to the newly created profile when
- * the provisioning of a managed profile has completed successfully. It is used in both the
- * Profile Owner and the Device Owner provisioning.
+ * Broadcast Action: This broadcast is sent to indicate that provisioning of a managed profile
+ * or managed device has completed successfully.
*
- * <p>The broadcast is limited to the DeviceAdminReceiver component specified in the message
- * that started the provisioning. It is also limited to the managed profile.
+ * <p>The broadcast is limited to the profile that will be managed by the application that
+ * requested provisioning. In the device owner case the profile is the primary user.
+ * The broadcast will also be limited to the {@link DeviceAdminReceiver} component
+ * specified in the original intent or NFC bump that started the provisioning process
+ * (@see DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE).
*
* <p>Input: Nothing.</p>
* <p>Output: Nothing</p>
@@ -219,6 +222,12 @@
* Called after the administrator is first enabled, as a result of
* receiving {@link #ACTION_DEVICE_ADMIN_ENABLED}. At this point you
* can use {@link DevicePolicyManager} to set your desired policies.
+ *
+ * <p> If the admin is activated by a device owner, then the intent
+ * may contain private extras that are relevant to user setup.
+ * {@see DevicePolicyManager#createAndInitializeUser(ComponentName, String, String,
+ * ComponentName, Intent)}
+ *
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
*/
@@ -307,18 +316,23 @@
}
/**
- * Called on the new profile when managed profile provisioning has completed.
- * Managed profile provisioning is the process of setting up the device so that it has a
- * separate profile which is managed by the mobile device management(mdm) application that
- * triggered the provisioning.
+ * Called when provisioning of a managed profile or managed device has completed successfully.
*
- * <p>As part of provisioning a new profile is created, the mdm is moved to the new profile and
- * set as the owner of the profile so that it has full control over it.
- * This intent is only received by the mdm package that is set as profile owner during
- * provisioning.
+ * <p> As a prerequisit for the execution of this callback the (@link DeviceAdminReceiver} has
+ * to declare an intent filter for {@link #ACTION_PROFILE_PROVISIONING_COMPLETE}.
+ * Its component must also be specified in the {@link DevicePolicyManager#EXTRA_DEVICE_ADMIN}
+ * of the {@link DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE} intent that started the
+ * managed provisioning.
*
- * <p>Provisioning can be triggered via an intent with the action
- * android.managedprovisioning.ACTION_PROVISION_MANAGED_PROFILE.
+ * <p>When provisioning is complete, the managed profile is hidden until the profile owner
+ * calls {DevicePolicyManager#setProfileEnabled(ComponentName admin)}. Typically a profile
+ * owner will enable the profile when it has finished any additional setup such as adding an
+ * account by using the {@link AccountManager} and calling apis to bring the profile into the
+ * desired state.
+ *
+ * <p> Note that provisioning completes without waiting for any server interactions, so the
+ * profile owner needs to wait for data to be available if required (e.g android device ids or
+ * other data that is set as a result of server interactions).
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b3b1d47..e80c761 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -26,6 +26,8 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.RestrictionsManager;
+import android.media.AudioService;
+import android.net.ProxyInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
@@ -83,18 +85,40 @@
}
/**
- * Activity action: Starts the provisioning flow which sets up a managed profile.
- * This intent will typically be sent by a mobile device management application(mdm).
- * Managed profile provisioning creates a profile, moves the mdm to the profile,
- * sets the mdm as the profile owner and removes all non required applications from the profile.
- * As a profile owner the mdm than has full control over the managed profile.
+ * Activity action: Used to indicate that the receiving activity is being started as part of the
+ * managed profile provisioning flow. This intent is typically sent to a mobile device
+ * management application (mdm) after the first part of the provisioning process is complete in
+ * the expectation that this app will (after optionally showing it's own UI) ultimately call
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} to complete the creation of the managed profile.
*
- * <p>The intent must contain the extras {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} and
- * {@link #EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME}.
+ * <p> The intent may contain the extras {@link #EXTRA_PROVISIONING_TOKEN} and
+ * {@link #EXTRA_PROVISIONING_EMAIL_ADDRESS}.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SEND_PROVISIONING_VALUES
+ = "android.app.action.ACTION_SEND_PROVISIONING_VALUES";
+
+ /**
+ * Activity action: Starts the provisioning flow which sets up a managed profile.
+ *
+ * <p>A managed profile allows data separation for example for the usage of a
+ * device as a personal and corporate device. The user which provisioning is started from and
+ * the managed profile share a launcher.
+ *
+ * <p>This intent will typically be sent by a mobile device management application (mdm).
+ * Provisioning adds a managed profile and sets the mdm as the profile owner who has full
+ * control over the profile
+ *
+ * <p>This intent must contain the extras {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}
+ * {@link #EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME} and {@link #EXTRA_DEVICE_ADMIN}.
*
* <p> When managed provisioning has completed, an intent of the type
* {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the
- * mdm app on the managed profile.
+ * managed profile. The intent is sent to the {@link DeviceAdminReceiver} specified in the
+ * {@link #EXTRA_DEVICE_ADMIN} exclusively.
+ *
+ * If provisioning fails, the managedProfile is removed so the device returns to its previous
+ * state.
*
* <p>Input: Nothing.</p>
* <p>Output: Nothing</p>
@@ -104,12 +128,35 @@
= "android.app.action.ACTION_PROVISION_MANAGED_PROFILE";
/**
+ * A broadcast intent with this action can be sent to ManagedProvisionning to specify that the
+ * user has already consented to the creation of the managed profile.
+ * The intent must contain the extras
+ * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} and
+ * {@link #EXTRA_PROVISIONING_TOKEN}
+ * @hide
+ */
+ public static final String ACTION_PROVISIONING_USER_HAS_CONSENTED
+ = "android.app.action.ACTION_PROVISIONING_USER_HAS_CONSENTED";
+
+ /**
* A String extra holding the name of the package of the mobile device management application
* that starts the managed provisioning flow. This package will be set as the profile owner.
* <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}.
*/
public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME
- = "deviceAdminPackageName";
+ = "android.app.extra.deviceAdminPackageName";
+
+ /**
+ * An int extra used to identify that during the current setup process the user has already
+ * consented to setting up a managed profile. This is typically received by
+ * a mobile device management application when it is started with
+ * {@link #ACTION_SEND_PROVISIONING_VALUES} and passed on in an intent
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} which starts the setup of the managed profile. The
+ * token indicates that steps asking for user consent can be skipped as the user has previously
+ * consented.
+ */
+ public static final String EXTRA_PROVISIONING_TOKEN
+ = "android.app.extra.token";
/**
* A String extra holding the default name of the profile that is created during managed profile
@@ -117,7 +164,18 @@
* <p>Use with {@link #ACTION_PROVISION_MANAGED_PROFILE}
*/
public static final String EXTRA_PROVISIONING_DEFAULT_MANAGED_PROFILE_NAME
- = "defaultManagedProfileName";
+ = "android.app.extra.defaultManagedProfileName";
+
+ /**
+ * A String extra holding the email address of the profile that is created during managed
+ * profile provisioning. This is typically received by a mobile management application when it
+ * is started with {@link #ACTION_SEND_PROVISIONING_VALUES} and passed on in an intent
+ * {@link #ACTION_PROVISION_MANAGED_PROFILE} which starts the setup of the managed profile. It
+ * is eventually passed on in an intent
+ * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE}.
+ */
+ public static final String EXTRA_PROVISIONING_EMAIL_ADDRESS
+ = "android.app.extra.ManagedProfileEmailAddress";
/**
* Activity action: ask the user to add a new device administrator to the system.
@@ -840,6 +898,9 @@
* {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD} to be able to call this
* method; if it has not, a security exception will be thrown.
*
+ * <p> Note that setting the password will automatically reset the expiration time for all
+ * active admins. Active admins do not need to explicitly call this method in that case.
+ *
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param timeout The limit (in ms) that a password can remain in effect. A value of 0
* means there is no restriction (unlimited).
@@ -1228,6 +1289,32 @@
}
/**
+ * Set a network-independent global HTTP proxy. This is not normally what you want
+ * for typical HTTP proxies - they are generally network dependent. However if you're
+ * 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.
+ *
+ * <p>This method requires the caller to be the device owner.
+ *
+ * <p>This proxy is only a recommendation and it is possible that some apps will ignore it.
+ * @see ProxyInfo
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated
+ * with.
+ * @param proxyInfo The a {@link ProxyInfo} object defining the new global
+ * HTTP proxy. A {@code null} value will clear the global HTTP proxy.
+ */
+ public void setRecommendedGlobalProxy(ComponentName admin, ProxyInfo proxyInfo) {
+ if (mService != null) {
+ try {
+ mService.setRecommendedGlobalProxy(admin, proxyInfo);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
* Returns the component name setting the global proxy.
* @return ComponentName object of the device admin that set the global proxy, or
* null if no admin has set the proxy.
@@ -1746,6 +1833,23 @@
return isDeviceOwnerApp(packageName);
}
+ /**
+ * Clears the current device owner. The caller must be the device owner.
+ *
+ * This function should be used cautiously as once it is called it cannot
+ * be undone. The device owner can only be set as a part of device setup
+ * before setup completes.
+ */
+ public void clearDeviceOwnerApp() {
+ if (mService != null) {
+ try {
+ mService.clearDeviceOwner(mContext.getPackageName());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to clear device owner");
+ }
+ }
+ }
+
/** @hide */
public String getDeviceOwner() {
if (mService != null) {
@@ -1958,6 +2062,8 @@
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param filter The {@link IntentFilter} the intent has to match to be also resolved in the
* other profile
+ * @param flags {@link DevicePolicyManager#FLAG_MANAGED_CAN_ACCESS_PARENT} and
+ * {@link DevicePolicyManager#FLAG_PARENT_CAN_ACCESS_MANAGED} are supported.
*/
public void addCrossProfileIntentFilter(ComponentName admin, IntentFilter filter, int flags) {
if (mService != null) {
@@ -2005,6 +2111,41 @@
}
/**
+ * Called by a device owner to create a user with the specified name. The UserHandle returned
+ * by this method should not be persisted as user handles are recycled as users are removed and
+ * created. If you need to persist an identifier for this user, use
+ * {@link UserManager#getSerialNumberForUser}. The new user will be started in the background
+ * immediately.
+ *
+ * <p> profileOwnerComponent is the {@link DeviceAdminReceiver} to be the profile owner as well
+ * as registered as an active admin on the new user. The profile owner package will be
+ * installed on the new user if it already is installed on the device.
+ *
+ * <p>If the optionalInitializeData is not null, then the extras will be passed to the
+ * profileOwnerComponent when onEnable is called.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param name the user's name
+ * @param ownerName the human readable name of the organisation associated with this DPM.
+ * @param profileOwnerComponent The {@link DeviceAdminReceiver} that will be an active admin on
+ * the user.
+ * @param adminExtras Extras that will be passed to onEnable of the admin receiver
+ * on the new user.
+ * @see UserHandle
+ * @return the UserHandle object for the created user, or null if the user could not be created.
+ */
+ public UserHandle createAndInitializeUser(ComponentName admin, String name, String ownerName,
+ ComponentName profileOwnerComponent, Bundle adminExtras) {
+ try {
+ return mService.createAndInitializeUser(admin, name, ownerName, profileOwnerComponent,
+ adminExtras);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not create a user", re);
+ }
+ return null;
+ }
+
+ /**
* Called by a device owner to remove a user and all associated data. The primary user can
* not be removed.
*
@@ -2152,45 +2293,6 @@
}
/**
- * 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 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;
- }
-
- /**
* 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
@@ -2340,4 +2442,37 @@
}
}
}
+
+ /**
+ * Called by profile or device owners to set the master volume mute on or off.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param on {@code true} to mute master volume, {@code false} to turn mute off.
+ */
+ public void setMasterVolumeMuted(ComponentName admin, boolean on) {
+ if (mService != null) {
+ try {
+ mService.setMasterVolumeMuted(admin, on);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to setMasterMute on device policy service");
+ }
+ }
+ }
+
+ /**
+ * Called by profile or device owners to check whether the master volume mute is on or off.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @return {@code true} if master volume is muted, {@code false} if it's not.
+ */
+ public boolean isMasterVolumeMuted(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.isMasterVolumeMuted(admin);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed to get isMasterMute on device policy service");
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7f754dc..a1caa21 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.ProxyInfo;
import android.os.Bundle;
import android.os.RemoteCallback;
import android.os.UserHandle;
@@ -78,6 +79,7 @@
ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList, int userHandle);
ComponentName getGlobalProxyAdmin(int userHandle);
+ void setRecommendedGlobalProxy(in ComponentName admin, in ProxyInfo proxyInfo);
int setStorageEncryption(in ComponentName who, boolean encrypt, int userHandle);
boolean getStorageEncryption(in ComponentName who, int userHandle);
@@ -106,6 +108,7 @@
boolean isDeviceOwner(String packageName);
String getDeviceOwner();
String getDeviceOwnerName();
+ void clearDeviceOwner(String packageName);
boolean setProfileOwner(String packageName, String ownerName, int userHandle);
String getProfileOwner(int userHandle);
@@ -133,11 +136,9 @@
boolean isApplicationBlocked(in ComponentName admin, in String packageName);
UserHandle createUser(in ComponentName who, in String name);
+ UserHandle createAndInitializeUser(in ComponentName who, in String name, in String profileOwnerName, in ComponentName profileOwnerComponent, in Bundle adminExtras);
boolean removeUser(in ComponentName who, in UserHandle userHandle);
- void 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();
@@ -147,4 +148,7 @@
void setGlobalSetting(in ComponentName who, in String setting, in String value);
void setSecureSetting(in ComponentName who, in String setting, in String value);
+
+ void setMasterVolumeMuted(in ComponentName admin, boolean on);
+ boolean isMasterVolumeMuted(in ComponentName admin);
}
diff --git a/core/java/android/app/job/IJobCallback.aidl b/core/java/android/app/job/IJobCallback.aidl
new file mode 100644
index 0000000..2d3948f
--- /dev/null
+++ b/core/java/android/app/job/IJobCallback.aidl
@@ -0,0 +1,53 @@
+/**
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+/**
+ * The server side of the JobScheduler IPC protocols. The app-side implementation
+ * invokes on this interface to indicate completion of the (asynchronous) instructions
+ * issued by the server.
+ *
+ * In all cases, the 'who' parameter is the caller's service binder, used to track
+ * which Job Service instance is reporting.
+ *
+ * {@hide}
+ */
+interface IJobCallback {
+ /**
+ * Immediate callback to the system after sending a start signal, used to quickly detect ANR.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param ongoing True to indicate that the client is processing the job. False if the job is
+ * complete
+ */
+ void acknowledgeStartMessage(int jobId, boolean ongoing);
+ /**
+ * Immediate callback to the system after sending a stop signal, used to quickly detect ANR.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param reschedule Whether or not to reschedule this job.
+ */
+ void acknowledgeStopMessage(int jobId, boolean reschedule);
+ /*
+ * Tell the job manager that the client is done with its execution, so that it can go on to
+ * the next one and stop attributing wakelock time to us etc.
+ *
+ * @param jobId Unique integer used to identify this job.
+ * @param reschedule Whether or not to reschedule this job.
+ */
+ void jobFinished(int jobId, boolean reschedule);
+}
diff --git a/core/java/android/app/job/IJobScheduler.aidl b/core/java/android/app/job/IJobScheduler.aidl
new file mode 100644
index 0000000..f1258ae
--- /dev/null
+++ b/core/java/android/app/job/IJobScheduler.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.app.job;
+
+import android.app.job.JobInfo;
+
+ /**
+ * IPC interface that supports the app-facing {@link #JobScheduler} api.
+ * {@hide}
+ */
+interface IJobScheduler {
+ int schedule(in JobInfo job);
+ void cancel(int jobId);
+ void cancelAll();
+ List<JobInfo> getAllPendingJobs();
+}
diff --git a/core/java/android/app/job/IJobService.aidl b/core/java/android/app/job/IJobService.aidl
new file mode 100644
index 0000000..63f8b81
--- /dev/null
+++ b/core/java/android/app/job/IJobService.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+import android.app.job.JobParameters;
+
+/**
+ * Interface that the framework uses to communicate with application code that implements a
+ * JobService. End user code does not implement this interface directly; instead, the app's
+ * service implementation will extend android.app.job.JobService.
+ * {@hide}
+ */
+oneway interface IJobService {
+ /** Begin execution of application's job. */
+ void startJob(in JobParameters jobParams);
+ /** Stop execution of application's task. */
+ void stopJob(in JobParameters jobParams);
+}
diff --git a/core/java/android/app/job/JobInfo.aidl b/core/java/android/app/job/JobInfo.aidl
new file mode 100644
index 0000000..7b198a8
--- /dev/null
+++ b/core/java/android/app/job/JobInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+parcelable JobInfo;
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
new file mode 100644
index 0000000..a22e4cd
--- /dev/null
+++ b/core/java/android/app/job/JobInfo.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+/**
+ * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the
+ * parameters required to schedule work against the calling application. These are constructed
+ * using the {@link JobInfo.Builder}.
+ */
+public class JobInfo implements Parcelable {
+ public interface NetworkType {
+ /** Default. */
+ public final int NONE = 0;
+ /** This job requires network connectivity. */
+ public final int ANY = 1;
+ /** This job requires network connectivity that is unmetered. */
+ public final int UNMETERED = 2;
+ }
+
+ /**
+ * Amount of backoff a job has initially by default, in milliseconds.
+ * @hide.
+ */
+ public static final long DEFAULT_INITIAL_BACKOFF_MILLIS = 5000L;
+
+ /**
+ * Default type of backoff.
+ * @hide
+ */
+ public static final int DEFAULT_BACKOFF_POLICY = BackoffPolicy.EXPONENTIAL;
+ /**
+ * Maximum backoff we allow for a job, in milliseconds.
+ * @hide
+ */
+ public static final long MAX_BACKOFF_DELAY_MILLIS = 24 * 60 * 60 * 1000; // 24 hours.
+
+ /**
+ * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1
+ * Expon: retry_time(failure_time, t) = failure_time + initial_retry_delay ^ t, t >= 1
+ */
+ public interface BackoffPolicy {
+ public final int LINEAR = 0;
+ public final int EXPONENTIAL = 1;
+ }
+
+ private final int jobId;
+ // TODO: Change this to use PersistableBundle when that lands in master.
+ private final PersistableBundle extras;
+ private final ComponentName service;
+ private final boolean requireCharging;
+ private final boolean requireDeviceIdle;
+ private final boolean hasEarlyConstraint;
+ private final boolean hasLateConstraint;
+ private final int networkCapabilities;
+ private final long minLatencyMillis;
+ private final long maxExecutionDelayMillis;
+ private final boolean isPeriodic;
+ private final long intervalMillis;
+ private final long initialBackoffMillis;
+ private final int backoffPolicy;
+
+ /**
+ * Unique job id associated with this class. This is assigned to your job by the scheduler.
+ */
+ public int getId() {
+ return jobId;
+ }
+
+ /**
+ * Bundle of extras which are returned to your application at execution time.
+ */
+ public PersistableBundle getExtras() {
+ return extras;
+ }
+
+ /**
+ * Name of the service endpoint that will be called back into by the JobScheduler.
+ */
+ public ComponentName getService() {
+ return service;
+ }
+
+ /**
+ * Whether this job needs the device to be plugged in.
+ */
+ public boolean isRequireCharging() {
+ return requireCharging;
+ }
+
+ /**
+ * Whether this job needs the device to be in an Idle maintenance window.
+ */
+ public boolean isRequireDeviceIdle() {
+ return requireDeviceIdle;
+ }
+
+ /**
+ * See {@link android.app.job.JobInfo.NetworkType} for a description of this value.
+ */
+ public int getNetworkCapabilities() {
+ return networkCapabilities;
+ }
+
+ /**
+ * Set for a job that does not recur periodically, to specify a delay after which the job
+ * will be eligible for execution. This value is not set if the job recurs periodically.
+ */
+ public long getMinLatencyMillis() {
+ return minLatencyMillis;
+ }
+
+ /**
+ * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs
+ * periodically.
+ */
+ public long getMaxExecutionDelayMillis() {
+ return maxExecutionDelayMillis;
+ }
+
+ /**
+ * Track whether this job will repeat with a given period.
+ */
+ public boolean isPeriodic() {
+ return isPeriodic;
+ }
+
+ /**
+ * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
+ * job does not recur periodically.
+ */
+ public long getIntervalMillis() {
+ return intervalMillis;
+ }
+
+ /**
+ * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
+ * will be increased depending on the backoff policy specified at job creation time. Defaults
+ * to 5 seconds.
+ */
+ public long getInitialBackoffMillis() {
+ return initialBackoffMillis;
+ }
+
+ /**
+ * See {@link android.app.job.JobInfo.BackoffPolicy} for an explanation of the values this field
+ * can take. This defaults to exponential.
+ */
+ public int getBackoffPolicy() {
+ return backoffPolicy;
+ }
+
+ /**
+ * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
+ * function was called at all.
+ * @hide
+ */
+ public boolean hasEarlyConstraint() {
+ return hasEarlyConstraint;
+ }
+
+ /**
+ * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
+ * function was called at all.
+ * @hide
+ */
+ public boolean hasLateConstraint() {
+ return hasLateConstraint;
+ }
+
+ private JobInfo(Parcel in) {
+ jobId = in.readInt();
+ extras = in.readPersistableBundle();
+ service = in.readParcelable(null);
+ requireCharging = in.readInt() == 1;
+ requireDeviceIdle = in.readInt() == 1;
+ networkCapabilities = in.readInt();
+ minLatencyMillis = in.readLong();
+ maxExecutionDelayMillis = in.readLong();
+ isPeriodic = in.readInt() == 1;
+ intervalMillis = in.readLong();
+ initialBackoffMillis = in.readLong();
+ backoffPolicy = in.readInt();
+ hasEarlyConstraint = in.readInt() == 1;
+ hasLateConstraint = in.readInt() == 1;
+ }
+
+ private JobInfo(JobInfo.Builder b) {
+ jobId = b.mJobId;
+ extras = b.mExtras;
+ service = b.mJobService;
+ requireCharging = b.mRequiresCharging;
+ requireDeviceIdle = b.mRequiresDeviceIdle;
+ networkCapabilities = b.mNetworkCapabilities;
+ minLatencyMillis = b.mMinLatencyMillis;
+ maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
+ isPeriodic = b.mIsPeriodic;
+ intervalMillis = b.mIntervalMillis;
+ initialBackoffMillis = b.mInitialBackoffMillis;
+ backoffPolicy = b.mBackoffPolicy;
+ hasEarlyConstraint = b.mHasEarlyConstraint;
+ hasLateConstraint = b.mHasLateConstraint;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(jobId);
+ out.writePersistableBundle(extras);
+ out.writeParcelable(service, flags);
+ out.writeInt(requireCharging ? 1 : 0);
+ out.writeInt(requireDeviceIdle ? 1 : 0);
+ out.writeInt(networkCapabilities);
+ out.writeLong(minLatencyMillis);
+ out.writeLong(maxExecutionDelayMillis);
+ out.writeInt(isPeriodic ? 1 : 0);
+ out.writeLong(intervalMillis);
+ out.writeLong(initialBackoffMillis);
+ out.writeInt(backoffPolicy);
+ out.writeInt(hasEarlyConstraint ? 1 : 0);
+ out.writeInt(hasLateConstraint ? 1 : 0);
+ }
+
+ public static final Creator<JobInfo> CREATOR = new Creator<JobInfo>() {
+ @Override
+ public JobInfo createFromParcel(Parcel in) {
+ return new JobInfo(in);
+ }
+
+ @Override
+ public JobInfo[] newArray(int size) {
+ return new JobInfo[size];
+ }
+ };
+
+ /** Builder class for constructing {@link JobInfo} objects. */
+ public static final class Builder {
+ private int mJobId;
+ private PersistableBundle mExtras = PersistableBundle.EMPTY;
+ private ComponentName mJobService;
+ // Requirements.
+ private boolean mRequiresCharging;
+ private boolean mRequiresDeviceIdle;
+ private int mNetworkCapabilities;
+ // One-off parameters.
+ private long mMinLatencyMillis;
+ private long mMaxExecutionDelayMillis;
+ // Periodic parameters.
+ private boolean mIsPeriodic;
+ private boolean mHasEarlyConstraint;
+ private boolean mHasLateConstraint;
+ private long mIntervalMillis;
+ // Back-off parameters.
+ private long mInitialBackoffMillis = DEFAULT_INITIAL_BACKOFF_MILLIS;
+ private int mBackoffPolicy = DEFAULT_BACKOFF_POLICY;
+ /** Easy way to track whether the client has tried to set a back-off policy. */
+ private boolean mBackoffPolicySet = false;
+
+ /**
+ * @param jobId Application-provided id for this job. Subsequent calls to cancel, or
+ * jobs created with the same jobId, will update the pre-existing job with
+ * the same id.
+ * @param jobService The endpoint that you implement that will receive the callback from the
+ * JobScheduler.
+ */
+ public Builder(int jobId, ComponentName jobService) {
+ mJobService = jobService;
+ mJobId = jobId;
+ }
+
+ /**
+ * Set optional extras. This is persisted, so we only allow primitive types.
+ * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+ */
+ public Builder setExtras(PersistableBundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Set some description of the kind of network capabilities you would like to have. This
+ * will be a parameter defined in {@link android.app.job.JobInfo.NetworkType}.
+ * Not calling this function means the network is not necessary.
+ * Bear in mind that calling this function defines network as a strict requirement for your
+ * job if the network requested is not available your job will never run. See
+ * {@link #setOverrideDeadline(long)} to change this behaviour.
+ */
+ public Builder setRequiredNetworkCapabilities(int networkCapabilities) {
+ mNetworkCapabilities = networkCapabilities;
+ return this;
+ }
+
+ /**
+ * Specify that to run this job, the device needs to be plugged in. This defaults to
+ * false.
+ * @param requiresCharging Whether or not the device is plugged in.
+ */
+ public Builder setRequiresCharging(boolean requiresCharging) {
+ mRequiresCharging = requiresCharging;
+ return this;
+ }
+
+ /**
+ * Specify that to run, the job needs the device to be in idle mode. This defaults to
+ * false.
+ * <p>Idle mode is a loose definition provided by the system, which means that the device
+ * is not in use, and has not been in use for some time. As such, it is a good time to
+ * perform resource heavy jobs. Bear in mind that battery usage will still be attributed
+ * to your application, and surfaced to the user in battery stats.</p>
+ * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
+ * window.
+ */
+ public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
+ mRequiresDeviceIdle = requiresDeviceIdle;
+ return this;
+ }
+
+ /**
+ * Specify that this job should recur with the provided interval, not more than once per
+ * period. You have no control over when within this interval this job will be executed,
+ * only the guarantee that it will be executed at most once within this interval.
+ * A periodic job will be repeated until the phone is turned off, however it will only be
+ * persisted beyond boot if the client app has declared the
+ * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
+ * periodic jobs without this permission, they simply will cease to exist after the phone
+ * restarts.
+ * Setting this function on the builder with {@link #setMinimumLatency(long)} or
+ * {@link #setOverrideDeadline(long)} will result in an error.
+ * @param intervalMillis Millisecond interval for which this job will repeat.
+ */
+ public Builder setPeriodic(long intervalMillis) {
+ mIsPeriodic = true;
+ mIntervalMillis = intervalMillis;
+ mHasEarlyConstraint = mHasLateConstraint = true;
+ return this;
+ }
+
+ /**
+ * Specify that this job should be delayed by the provided amount of time.
+ * Because it doesn't make sense setting this property on a periodic job, doing so will
+ * throw an {@link java.lang.IllegalArgumentException} when
+ * {@link android.app.job.JobInfo.Builder#build()} is called.
+ * @param minLatencyMillis Milliseconds before which this job will not be considered for
+ * execution.
+ */
+ public Builder setMinimumLatency(long minLatencyMillis) {
+ mMinLatencyMillis = minLatencyMillis;
+ mHasEarlyConstraint = true;
+ return this;
+ }
+
+ /**
+ * Set deadline which is the maximum scheduling latency. The job will be run by this
+ * deadline even if other requirements are not met. Because it doesn't make sense setting
+ * this property on a periodic job, doing so will throw an
+ * {@link java.lang.IllegalArgumentException} when
+ * {@link android.app.job.JobInfo.Builder#build()} is called.
+ */
+ public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
+ mMaxExecutionDelayMillis = maxExecutionDelayMillis;
+ mHasLateConstraint = true;
+ return this;
+ }
+
+ /**
+ * Set up the back-off/retry policy.
+ * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at
+ * 1hr.
+ * Note that trying to set a backoff criteria for a job with
+ * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
+ * This is because back-off typically does not make sense for these types of jobs. See
+ * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
+ * for more description of the return value for the case of a job executing while in idle
+ * mode.
+ * @param initialBackoffMillis Millisecond time interval to wait initially when job has
+ * failed.
+ * @param backoffPolicy is one of {@link BackoffPolicy}
+ */
+ public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
+ mBackoffPolicySet = true;
+ mInitialBackoffMillis = initialBackoffMillis;
+ mBackoffPolicy = backoffPolicy;
+ return this;
+ }
+
+ /**
+ * @return The job object to hand to the JobScheduler. This object is immutable.
+ */
+ public JobInfo build() {
+ mExtras = new PersistableBundle(mExtras); // Make our own copy.
+ // Check that a deadline was not set on a periodic job.
+ if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
+ throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
+ "periodic job.");
+ }
+ if (mIsPeriodic && (mMinLatencyMillis != 0L)) {
+ throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
+ "periodic job");
+ }
+ if (mBackoffPolicySet && mRequiresDeviceIdle) {
+ throw new IllegalArgumentException("An idle mode job will not respect any" +
+ " back-off policy, so calling setBackoffCriteria with" +
+ " setRequiresDeviceIdle is an error.");
+ }
+ return new JobInfo(this);
+ }
+ }
+
+}
diff --git a/core/java/android/app/job/JobParameters.aidl b/core/java/android/app/job/JobParameters.aidl
new file mode 100644
index 0000000..e7551b9
--- /dev/null
+++ b/core/java/android/app/job/JobParameters.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.job;
+
+parcelable JobParameters;
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
new file mode 100644
index 0000000..724856a
--- /dev/null
+++ b/core/java/android/app/job/JobParameters.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import android.app.job.IJobCallback;
+import android.app.job.IJobCallback.Stub;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+/**
+ * Contains the parameters used to configure/identify your job. You do not create this object
+ * yourself, instead it is handed in to your application by the System.
+ */
+public class JobParameters implements Parcelable {
+
+ private final int jobId;
+ private final PersistableBundle extras;
+ private final IBinder callback;
+
+ /** @hide */
+ public JobParameters(int jobId, PersistableBundle extras, IBinder callback) {
+ this.jobId = jobId;
+ this.extras = extras;
+ this.callback = callback;
+ }
+
+ /**
+ * @return The unique id of this job, specified at creation time.
+ */
+ public int getJobId() {
+ return jobId;
+ }
+
+ /**
+ * @return The extras you passed in when constructing this job with
+ * {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
+ * never be null. If you did not set any extras this will be an empty bundle.
+ */
+ public PersistableBundle getExtras() {
+ return extras;
+ }
+
+ /** @hide */
+ public IJobCallback getCallback() {
+ return IJobCallback.Stub.asInterface(callback);
+ }
+
+ private JobParameters(Parcel in) {
+ jobId = in.readInt();
+ extras = in.readPersistableBundle();
+ callback = in.readStrongBinder();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(jobId);
+ dest.writePersistableBundle(extras);
+ dest.writeStrongBinder(callback);
+ }
+
+ public static final Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
+ @Override
+ public JobParameters createFromParcel(Parcel in) {
+ return new JobParameters(in);
+ }
+
+ @Override
+ public JobParameters[] newArray(int size) {
+ return new JobParameters[size];
+ }
+ };
+}
diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java
new file mode 100644
index 0000000..7fe192c
--- /dev/null
+++ b/core/java/android/app/job/JobScheduler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import java.util.List;
+
+import android.content.Context;
+
+/**
+ * Class for scheduling various types of jobs with the scheduling framework on the device.
+ *
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}.
+ */
+public abstract class JobScheduler {
+ /**
+ * Returned from {@link #schedule(JobInfo)} when an invalid parameter was supplied. This can occur
+ * if the run-time for your job is too short, or perhaps the system can't resolve the
+ * requisite {@link JobService} in your package.
+ */
+ public static final int RESULT_FAILURE = 0;
+ /**
+ * Returned from {@link #schedule(JobInfo)} if this application has made too many requests for
+ * work over too short a time.
+ */
+ // TODO: Determine if this is necessary.
+ public static final int RESULT_SUCCESS = 1;
+
+ /**
+ * @param job The job you wish scheduled. See
+ * {@link android.app.job.JobInfo.Builder JobInfo.Builder} for more detail on the sorts of jobs
+ * you can schedule.
+ * @return If >0, this int returns the jobId of the successfully scheduled job.
+ * Otherwise you have to compare the return value to the error codes defined in this class.
+ */
+ public abstract int schedule(JobInfo job);
+
+ /**
+ * Cancel a job that is pending in the JobScheduler.
+ * @param jobId unique identifier for this job. Obtain this value from the jobs returned by
+ * {@link #getAllPendingJobs()}.
+ * @return
+ */
+ public abstract void cancel(int jobId);
+
+ /**
+ * Cancel all jobs that have been registered with the JobScheduler by this package.
+ */
+ public abstract void cancelAll();
+
+ /**
+ * @return a list of all the jobs registered by this package that have not yet been executed.
+ */
+ public abstract List<JobInfo> getAllPendingJobs();
+
+}
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
new file mode 100644
index 0000000..eea0268
--- /dev/null
+++ b/core/java/android/app/job/JobService.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import android.app.Service;
+import android.app.job.IJobCallback;
+import android.app.job.IJobService;
+import android.app.job.IJobService.Stub;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p>
+ * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
+ * are responsible for overriding {@link JobService#onStartJob(JobParameters)}, which is where
+ * you will implement your job logic.</p>
+ * <p>This service executes each incoming job on a {@link android.os.Handler} running on your
+ * application's main thread. This means that you <b>must</b> offload your execution logic to
+ * another thread/handler/{@link android.os.AsyncTask} of your choosing. Not doing so will result
+ * in blocking any future callbacks from the JobManager - specifically
+ * {@link #onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the
+ * scheduling requirements are no longer being met.</p>
+ */
+public abstract class JobService extends Service {
+ private static final String TAG = "JobService";
+
+ /**
+ * Job services must be protected with this permission:
+ *
+ * <pre class="prettyprint">
+ * <service android:name="MyJobService"
+ * android:permission="android.permission.BIND_JOB_SERVICE" >
+ * ...
+ * </service>
+ * </pre>
+ *
+ * <p>If a job service is declared in the manifest but not protected with this
+ * permission, that service will be ignored by the OS.
+ */
+ public static final String PERMISSION_BIND =
+ "android.permission.BIND_JOB_SERVICE";
+
+ /**
+ * Identifier for a message that will result in a call to
+ * {@link #onStartJob(android.app.job.JobParameters)}.
+ */
+ private final int MSG_EXECUTE_JOB = 0;
+ /**
+ * Message that will result in a call to {@link #onStopJob(android.app.job.JobParameters)}.
+ */
+ private final int MSG_STOP_JOB = 1;
+ /**
+ * Message that the client has completed execution of this job.
+ */
+ private final int MSG_JOB_FINISHED = 2;
+
+ /** Lock object for {@link #mHandler}. */
+ private final Object mHandlerLock = new Object();
+
+ /**
+ * Handler we post jobs to. Responsible for calling into the client logic, and handling the
+ * callback to the system.
+ */
+ @GuardedBy("mHandlerLock")
+ JobHandler mHandler;
+
+ /** Binder for this service. */
+ IJobService mBinder = new IJobService.Stub() {
+ @Override
+ public void startJob(JobParameters jobParams) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams);
+ m.sendToTarget();
+ }
+ @Override
+ public void stopJob(JobParameters jobParams) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);
+ m.sendToTarget();
+ }
+ };
+
+ /** @hide */
+ void ensureHandler() {
+ synchronized (mHandlerLock) {
+ if (mHandler == null) {
+ mHandler = new JobHandler(getMainLooper());
+ }
+ }
+ }
+
+ /**
+ * Runs on application's main thread - callbacks are meant to offboard work to some other
+ * (app-specified) mechanism.
+ * @hide
+ */
+ class JobHandler extends Handler {
+ JobHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final JobParameters params = (JobParameters) msg.obj;
+ switch (msg.what) {
+ case MSG_EXECUTE_JOB:
+ try {
+ boolean workOngoing = JobService.this.onStartJob(params);
+ ackStartMessage(params, workOngoing);
+ } catch (Exception e) {
+ Log.e(TAG, "Error while executing job: " + params.getJobId());
+ throw new RuntimeException(e);
+ }
+ break;
+ case MSG_STOP_JOB:
+ try {
+ boolean ret = JobService.this.onStopJob(params);
+ ackStopMessage(params, ret);
+ } catch (Exception e) {
+ Log.e(TAG, "Application unable to handle onStopJob.", e);
+ throw new RuntimeException(e);
+ }
+ break;
+ case MSG_JOB_FINISHED:
+ final boolean needsReschedule = (msg.arg2 == 1);
+ IJobCallback callback = params.getCallback();
+ if (callback != null) {
+ try {
+ callback.jobFinished(params.getJobId(), needsReschedule);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error reporting job finish to system: binder has gone" +
+ "away.");
+ }
+ } else {
+ Log.e(TAG, "finishJob() called for a nonexistent job id.");
+ }
+ break;
+ default:
+ Log.e(TAG, "Unrecognised message received.");
+ break;
+ }
+ }
+
+ private void ackStartMessage(JobParameters params, boolean workOngoing) {
+ final IJobCallback callback = params.getCallback();
+ final int jobId = params.getJobId();
+ if (callback != null) {
+ try {
+ callback.acknowledgeStartMessage(jobId, workOngoing);
+ } catch(RemoteException e) {
+ Log.e(TAG, "System unreachable for starting job.");
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Attempting to ack a job that has already been processed.");
+ }
+ }
+ }
+
+ private void ackStopMessage(JobParameters params, boolean reschedule) {
+ final IJobCallback callback = params.getCallback();
+ final int jobId = params.getJobId();
+ if (callback != null) {
+ try {
+ callback.acknowledgeStopMessage(jobId, reschedule);
+ } catch(RemoteException e) {
+ Log.e(TAG, "System unreachable for stopping job.");
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Attempting to ack a job that has already been processed.");
+ }
+ }
+ }
+ }
+
+ /** @hide */
+ public final IBinder onBind(Intent intent) {
+ return mBinder.asBinder();
+ }
+
+ /**
+ * Override this method with the callback logic for your job. Any such logic needs to be
+ * performed on a separate thread, as this function is executed on your application's main
+ * thread.
+ *
+ * @param params Parameters specifying info about this job, including the extras bundle you
+ * optionally provided at job-creation time.
+ * @return True if your service needs to process the work (on a separate thread). False if
+ * there's no more work to be done for this job.
+ */
+ public abstract boolean onStartJob(JobParameters params);
+
+ /**
+ * This method is called if the system has determined that you must stop execution of your job
+ * even before you've had a chance to call {@link #jobFinished(JobParameters, boolean)}.
+ *
+ * <p>This will happen if the requirements specified at schedule time are no longer met. For
+ * example you may have requested WiFi with
+ * {@link android.app.job.JobInfo.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+ * job was executing the user toggled WiFi. Another example is if you had specified
+ * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
+ * idle maintenance window. You are solely responsible for the behaviour of your application
+ * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
+ * immediate repercussion is that the system will cease holding a wakelock for you.</p>
+ *
+ * @param params Parameters specifying info about this job.
+ * @return True to indicate to the JobManager whether you'd like to reschedule this job based
+ * on the retry criteria provided at job creation-time. False to drop the job. Regardless of
+ * the value returned, your job must stop executing.
+ */
+ public abstract boolean onStopJob(JobParameters params);
+
+ /**
+ * Callback to inform the JobManager you've finished executing. This can be called from any
+ * thread, as it will ultimately be run on your application's main thread. When the system
+ * receives this message it will release the wakelock being held.
+ * <p>
+ * You can specify post-execution behaviour to the scheduler here with
+ * <code>needsReschedule </code>. This will apply a back-off timer to your job based on
+ * the default, or what was set with
+ * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}. The original
+ * requirements are always honoured even for a backed-off job. Note that a job running in
+ * idle mode will not be backed-off. Instead what will happen is the job will be re-added
+ * to the queue and re-executed within a future idle maintenance window.
+ * </p>
+ *
+ * @param params Parameters specifying system-provided info about this job, this was given to
+ * your application in {@link #onStartJob(JobParameters)}.
+ * @param needsReschedule True if this job is complete, false if you want the JobManager to
+ * reschedule you.
+ */
+ public final void jobFinished(JobParameters params, boolean needsReschedule) {
+ ensureHandler();
+ Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params);
+ m.arg2 = needsReschedule ? 1 : 0;
+ m.sendToTarget();
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/app/maintenance/package.html b/core/java/android/app/maintenance/package.html
new file mode 100644
index 0000000..1c9bf9d
--- /dev/null
+++ b/core/java/android/app/maintenance/package.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+ {@hide}
+</body>
+</html>
diff --git a/core/java/android/app/task/ITaskCallback.aidl b/core/java/android/app/task/ITaskCallback.aidl
deleted file mode 100644
index d8a32fd..0000000
--- a/core/java/android/app/task/ITaskCallback.aidl
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.task;
-
-import android.app.task.ITaskService;
-import android.app.task.TaskParams;
-
-/**
- * The server side of the TaskManager IPC protocols. The app-side implementation
- * invokes on this interface to indicate completion of the (asynchronous) instructions
- * issued by the server.
- *
- * In all cases, the 'who' parameter is the caller's service binder, used to track
- * which Task Service instance is reporting.
- *
- * {@hide}
- */
-interface ITaskCallback {
- /**
- * Immediate callback to the system after sending a start signal, used to quickly detect ANR.
- *
- * @param taskId Unique integer used to identify this task.
- * @param ongoing True to indicate that the client is processing the task. False if the task is
- * complete
- */
- void acknowledgeStartMessage(int taskId, boolean ongoing);
- /**
- * Immediate callback to the system after sending a stop signal, used to quickly detect ANR.
- *
- * @param taskId Unique integer used to identify this task.
- * @param rescheulde Whether or not to reschedule this task.
- */
- void acknowledgeStopMessage(int taskId, boolean reschedule);
- /*
- * Tell the task manager that the client is done with its execution, so that it can go on to
- * the next one and stop attributing wakelock time to us etc.
- *
- * @param taskId Unique integer used to identify this task.
- * @param reschedule Whether or not to reschedule this task.
- */
- void taskFinished(int taskId, boolean reschedule);
-}
diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/task/ITaskManager.aidl
deleted file mode 100644
index b56c78a..0000000
--- a/core/java/android/app/task/ITaskManager.aidl
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.task;
-
-import android.app.task.Task;
-
- /**
- * IPC interface that supports the app-facing {@link #TaskManager} api.
- * {@hide}
- */
-interface ITaskManager {
- int schedule(in Task task);
- void cancel(int taskId);
- void cancelAll();
- List<Task> getAllPendingTasks();
-}
diff --git a/core/java/android/app/task/ITaskService.aidl b/core/java/android/app/task/ITaskService.aidl
deleted file mode 100644
index 87b0191..0000000
--- a/core/java/android/app/task/ITaskService.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.task;
-
-import android.app.task.ITaskCallback;
-import android.app.task.TaskParams;
-
-import android.os.Bundle;
-
-/**
- * Interface that the framework uses to communicate with application code that implements a
- * TaskService. End user code does not implement this interface directly; instead, the app's
- * service implementation will extend android.app.task.TaskService.
- * {@hide}
- */
-oneway interface ITaskService {
- /** Begin execution of application's task. */
- void startTask(in TaskParams taskParams);
- /** Stop execution of application's task. */
- void stopTask(in TaskParams taskParams);
-}
diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/task/Task.aidl
deleted file mode 100644
index 1f25439..0000000
--- a/core/java/android/app/task/Task.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.task;
-
-parcelable Task;
-
\ No newline at end of file
diff --git a/core/java/android/app/task/Task.java b/core/java/android/app/task/Task.java
deleted file mode 100644
index ca4aeb2..0000000
--- a/core/java/android/app/task/Task.java
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.app.task;
-
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the
- * parameters required to schedule work against the calling application. These are constructed
- * using the {@link Task.Builder}.
- */
-public class Task implements Parcelable {
- public interface NetworkType {
- /** Default. */
- public final int NONE = 0;
- /** This task requires network connectivity. */
- public final int ANY = 1;
- /** This task requires network connectivity that is unmetered. */
- public final int UNMETERED = 2;
- }
-
- /**
- * Linear: retry_time(failure_time, t) = failure_time + initial_retry_delay * t, t >= 1
- * Expon: retry_time(failure_time, t) = failure_time + initial_retry_delay ^ t, t >= 1
- */
- public interface BackoffPolicy {
- public final int LINEAR = 0;
- public final int EXPONENTIAL = 1;
- }
-
- private final int taskId;
- // TODO: Change this to use PersistableBundle when that lands in master.
- private final Bundle extras;
- private final ComponentName service;
- private final boolean requireCharging;
- private final boolean requireDeviceIdle;
- private final boolean hasEarlyConstraint;
- private final boolean hasLateConstraint;
- private final int networkCapabilities;
- private final long minLatencyMillis;
- private final long maxExecutionDelayMillis;
- private final boolean isPeriodic;
- private final long intervalMillis;
- private final long initialBackoffMillis;
- private final int backoffPolicy;
-
- /**
- * Unique task id associated with this class. This is assigned to your task by the scheduler.
- */
- public int getId() {
- return taskId;
- }
-
- /**
- * Bundle of extras which are returned to your application at execution time.
- */
- public Bundle getExtras() {
- return extras;
- }
-
- /**
- * Name of the service endpoint that will be called back into by the TaskManager.
- */
- public ComponentName getService() {
- return service;
- }
-
- /**
- * Whether this task needs the device to be plugged in.
- */
- public boolean isRequireCharging() {
- return requireCharging;
- }
-
- /**
- * Whether this task needs the device to be in an Idle maintenance window.
- */
- public boolean isRequireDeviceIdle() {
- return requireDeviceIdle;
- }
-
- /**
- * See {@link android.app.task.Task.NetworkType} for a description of this value.
- */
- public int getNetworkCapabilities() {
- return networkCapabilities;
- }
-
- /**
- * Set for a task that does not recur periodically, to specify a delay after which the task
- * will be eligible for execution. This value is not set if the task recurs periodically.
- */
- public long getMinLatencyMillis() {
- return minLatencyMillis;
- }
-
- /**
- * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the task recurs
- * periodically.
- */
- public long getMaxExecutionDelayMillis() {
- return maxExecutionDelayMillis;
- }
-
- /**
- * Track whether this task will repeat with a given period.
- */
- public boolean isPeriodic() {
- return isPeriodic;
- }
-
- /**
- * Set to the interval between occurrences of this task. This value is <b>not</b> set if the
- * task does not recur periodically.
- */
- public long getIntervalMillis() {
- return intervalMillis;
- }
-
- /**
- * The amount of time the TaskManager will wait before rescheduling a failed task. This value
- * will be increased depending on the backoff policy specified at task creation time. Defaults
- * to 5 seconds.
- */
- public long getInitialBackoffMillis() {
- return initialBackoffMillis;
- }
-
- /**
- * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field
- * can take. This defaults to exponential.
- */
- public int getBackoffPolicy() {
- return backoffPolicy;
- }
-
- /**
- * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
- * function was called at all.
- * @hide
- */
- public boolean hasEarlyConstraint() {
- return hasEarlyConstraint;
- }
-
- /**
- * User can specify a late constraint of 0L, which is valid, so we keep track of whether the
- * function was called at all.
- * @hide
- */
- public boolean hasLateConstraint() {
- return hasLateConstraint;
- }
-
- private Task(Parcel in) {
- taskId = in.readInt();
- extras = in.readBundle();
- service = ComponentName.readFromParcel(in);
- requireCharging = in.readInt() == 1;
- requireDeviceIdle = in.readInt() == 1;
- networkCapabilities = in.readInt();
- minLatencyMillis = in.readLong();
- maxExecutionDelayMillis = in.readLong();
- isPeriodic = in.readInt() == 1;
- intervalMillis = in.readLong();
- initialBackoffMillis = in.readLong();
- backoffPolicy = in.readInt();
- hasEarlyConstraint = in.readInt() == 1;
- hasLateConstraint = in.readInt() == 1;
- }
-
- private Task(Task.Builder b) {
- taskId = b.mTaskId;
- extras = new Bundle(b.mExtras);
- service = b.mTaskService;
- requireCharging = b.mRequiresCharging;
- requireDeviceIdle = b.mRequiresDeviceIdle;
- networkCapabilities = b.mNetworkCapabilities;
- minLatencyMillis = b.mMinLatencyMillis;
- maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
- isPeriodic = b.mIsPeriodic;
- intervalMillis = b.mIntervalMillis;
- initialBackoffMillis = b.mInitialBackoffMillis;
- backoffPolicy = b.mBackoffPolicy;
- hasEarlyConstraint = b.mHasEarlyConstraint;
- hasLateConstraint = b.mHasLateConstraint;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(taskId);
- out.writeBundle(extras);
- ComponentName.writeToParcel(service, out);
- out.writeInt(requireCharging ? 1 : 0);
- out.writeInt(requireDeviceIdle ? 1 : 0);
- out.writeInt(networkCapabilities);
- out.writeLong(minLatencyMillis);
- out.writeLong(maxExecutionDelayMillis);
- out.writeInt(isPeriodic ? 1 : 0);
- out.writeLong(intervalMillis);
- out.writeLong(initialBackoffMillis);
- out.writeInt(backoffPolicy);
- out.writeInt(hasEarlyConstraint ? 1 : 0);
- out.writeInt(hasLateConstraint ? 1 : 0);
- }
-
- public static final Creator<Task> CREATOR = new Creator<Task>() {
- @Override
- public Task createFromParcel(Parcel in) {
- return new Task(in);
- }
-
- @Override
- public Task[] newArray(int size) {
- return new Task[size];
- }
- };
-
- /**
- * Builder class for constructing {@link Task} objects.
- */
- public static final class Builder {
- private int mTaskId;
- private Bundle mExtras;
- private ComponentName mTaskService;
- // Requirements.
- private boolean mRequiresCharging;
- private boolean mRequiresDeviceIdle;
- private int mNetworkCapabilities;
- // One-off parameters.
- private long mMinLatencyMillis;
- private long mMaxExecutionDelayMillis;
- // Periodic parameters.
- private boolean mIsPeriodic;
- private boolean mHasEarlyConstraint;
- private boolean mHasLateConstraint;
- private long mIntervalMillis;
- // Back-off parameters.
- private long mInitialBackoffMillis = 5000L;
- private int mBackoffPolicy = BackoffPolicy.EXPONENTIAL;
- /** Easy way to track whether the client has tried to set a back-off policy. */
- private boolean mBackoffPolicySet = false;
-
- /**
- * @param taskId Application-provided id for this task. Subsequent calls to cancel, or
- * tasks created with the same taskId, will update the pre-existing task with
- * the same id.
- * @param taskService The endpoint that you implement that will receive the callback from the
- * TaskManager.
- */
- public Builder(int taskId, ComponentName taskService) {
- mTaskService = taskService;
- mTaskId = taskId;
- }
-
- /**
- * Set optional extras. This is persisted, so we only allow primitive types.
- * @param extras Bundle containing extras you want the scheduler to hold on to for you.
- */
- public Builder setExtras(Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Set some description of the kind of network capabilities you would like to have. This
- * will be a parameter defined in {@link android.app.task.Task.NetworkType}.
- * Not calling this function means the network is not necessary.
- * Bear in mind that calling this function defines network as a strict requirement for your
- * task if the network requested is not available your task will never run. See
- * {@link #setOverrideDeadline(long)} to change this behaviour.
- */
- public Builder setRequiredNetworkCapabilities(int networkCapabilities) {
- mNetworkCapabilities = networkCapabilities;
- return this;
- }
-
- /*
- * Specify that to run this task, the device needs to be plugged in. This defaults to
- * false.
- * @param requireCharging Whether or not the device is plugged in.
- */
- public Builder setRequiresCharging(boolean requiresCharging) {
- mRequiresCharging = requiresCharging;
- return this;
- }
-
- /**
- * Specify that to run, the task needs the device to be in idle mode. This defaults to
- * false.
- * <p>Idle mode is a loose definition provided by the system, which means that the device
- * is not in use, and has not been in use for some time. As such, it is a good time to
- * perform resource heavy tasks. Bear in mind that battery usage will still be attributed
- * to your application, and surfaced to the user in battery stats.</p>
- * @param requiresDeviceIdle Whether or not the device need be within an idle maintenance
- * window.
- */
- public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
- mRequiresDeviceIdle = requiresDeviceIdle;
- return this;
- }
-
- /**
- * Specify that this task should recur with the provided interval, not more than once per
- * period. You have no control over when within this interval this task will be executed,
- * only the guarantee that it will be executed at most once within this interval.
- * A periodic task will be repeated until the phone is turned off, however it will only be
- * persisted beyond boot if the client app has declared the
- * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
- * periodic tasks without this permission, they simply will cease to exist after the phone
- * restarts.
- * Setting this function on the builder with {@link #setMinimumLatency(long)} or
- * {@link #setOverrideDeadline(long)} will result in an error.
- * @param intervalMillis Millisecond interval for which this task will repeat.
- */
- public Builder setPeriodic(long intervalMillis) {
- mIsPeriodic = true;
- mIntervalMillis = intervalMillis;
- mHasEarlyConstraint = mHasLateConstraint = true;
- return this;
- }
-
- /**
- * Specify that this task should be delayed by the provided amount of time.
- * Because it doesn't make sense setting this property on a periodic task, doing so will
- * throw an {@link java.lang.IllegalArgumentException} when
- * {@link android.app.task.Task.Builder#build()} is called.
- * @param minLatencyMillis Milliseconds before which this task will not be considered for
- * execution.
- */
- public Builder setMinimumLatency(long minLatencyMillis) {
- mMinLatencyMillis = minLatencyMillis;
- mHasEarlyConstraint = true;
- return this;
- }
-
- /**
- * Set deadline which is the maximum scheduling latency. The task will be run by this
- * deadline even if other requirements are not met. Because it doesn't make sense setting
- * this property on a periodic task, doing so will throw an
- * {@link java.lang.IllegalArgumentException} when
- * {@link android.app.task.Task.Builder#build()} is called.
- */
- public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
- mMaxExecutionDelayMillis = maxExecutionDelayMillis;
- mHasLateConstraint = true;
- return this;
- }
-
- /**
- * Set up the back-off/retry policy.
- * This defaults to some respectable values: {5 seconds, Exponential}. We cap back-off at
- * 1hr.
- * Note that trying to set a backoff criteria for a task with
- * {@link #setRequiresDeviceIdle(boolean)} will throw an exception when you call build().
- * This is because back-off typically does not make sense for these types of tasks. See
- * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)}
- * for more description of the return value for the case of a task executing while in idle
- * mode.
- * @param initialBackoffMillis Millisecond time interval to wait initially when task has
- * failed.
- * @param backoffPolicy is one of {@link BackoffPolicy}
- */
- public Builder setBackoffCriteria(long initialBackoffMillis, int backoffPolicy) {
- mBackoffPolicySet = true;
- mInitialBackoffMillis = initialBackoffMillis;
- mBackoffPolicy = backoffPolicy;
- return this;
- }
-
- /**
- * @return The task object to hand to the TaskManager. This object is immutable.
- */
- public Task build() {
- if (mExtras == null) {
- mExtras = Bundle.EMPTY;
- }
- if (mTaskId < 0) {
- throw new IllegalArgumentException("Task id must be greater than 0.");
- }
- // Check that a deadline was not set on a periodic task.
- if (mIsPeriodic && mHasLateConstraint) {
- throw new IllegalArgumentException("Can't call setOverrideDeadline() on a " +
- "periodic task.");
- }
- if (mIsPeriodic && mHasEarlyConstraint) {
- throw new IllegalArgumentException("Can't call setMinimumLatency() on a " +
- "periodic task");
- }
- if (mBackoffPolicySet && mRequiresDeviceIdle) {
- throw new IllegalArgumentException("An idle mode task will not respect any" +
- " back-off policy, so calling setBackoffCriteria with" +
- " setRequiresDeviceIdle is an error.");
- }
- return new Task(this);
- }
- }
-
-}
diff --git a/core/java/android/app/task/TaskManager.java b/core/java/android/app/task/TaskManager.java
deleted file mode 100644
index 00f57da..0000000
--- a/core/java/android/app/task/TaskManager.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.app.task;
-
-import java.util.List;
-
-import android.content.Context;
-
-/**
- * Class for scheduling various types of tasks with the scheduling framework on the device.
- *
- * <p>You do not
- * instantiate this class directly; instead, retrieve it through
- * {@link android.content.Context#getSystemService
- * Context.getSystemService(Context.TASK_SERVICE)}.
- */
-public abstract class TaskManager {
- /*
- * Returned from {@link #schedule(Task)} when an invalid parameter was supplied. This can occur
- * if the run-time for your task is too short, or perhaps the system can't resolve the
- * requisite {@link TaskService} in your package.
- */
- public static final int RESULT_FAILURE = 0;
- /**
- * Returned from {@link #schedule(Task)} if this application has made too many requests for
- * work over too short a time.
- */
- // TODO: Determine if this is necessary.
- public static final int RESULT_SUCCESS = 1;
-
- /**
- * @param task The task you wish scheduled. See
- * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks
- * you can schedule.
- * @return If >0, this int returns the taskId of the successfully scheduled task.
- * Otherwise you have to compare the return value to the error codes defined in this class.
- */
- public abstract int schedule(Task task);
-
- /**
- * Cancel a task that is pending in the TaskManager.
- * @param taskId unique identifier for this task. Obtain this value from the tasks returned by
- * {@link #getAllPendingTasks()}.
- * @return
- */
- public abstract void cancel(int taskId);
-
- /**
- * Cancel all tasks that have been registered with the TaskManager by this package.
- */
- public abstract void cancelAll();
-
- /**
- * @return a list of all the tasks registered by this package that have not yet been executed.
- */
- public abstract List<Task> getAllPendingTasks();
-
-}
diff --git a/core/java/android/app/task/TaskParams.aidl b/core/java/android/app/task/TaskParams.aidl
deleted file mode 100644
index 9b25855..0000000
--- a/core/java/android/app/task/TaskParams.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.task;
-
-parcelable TaskParams;
\ No newline at end of file
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
deleted file mode 100644
index dacb3480..0000000
--- a/core/java/android/app/task/TaskParams.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.app.task;
-
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Contains the parameters used to configure/identify your task. You do not create this object
- * yourself, instead it is handed in to your application by the System.
- */
-public class TaskParams implements Parcelable {
-
- private final int taskId;
- private final Bundle extras;
- private final IBinder callback;
-
- /** @hide */
- public TaskParams(int taskId, Bundle extras, IBinder callback) {
- this.taskId = taskId;
- this.extras = extras;
- this.callback = callback;
- }
-
- /**
- * @return The unique id of this task, specified at creation time.
- */
- public int getTaskId() {
- return taskId;
- }
-
- /**
- * @return The extras you passed in when constructing this task with
- * {@link android.app.task.Task.Builder#setExtras(android.os.Bundle)}. This will
- * never be null. If you did not set any extras this will be an empty bundle.
- */
- public Bundle getExtras() {
- return extras;
- }
-
- /** @hide */
- public ITaskCallback getCallback() {
- return ITaskCallback.Stub.asInterface(callback);
- }
-
- private TaskParams(Parcel in) {
- taskId = in.readInt();
- extras = in.readBundle();
- callback = in.readStrongBinder();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(taskId);
- dest.writeBundle(extras);
- dest.writeStrongBinder(callback);
- }
-
- public static final Creator<TaskParams> CREATOR = new Creator<TaskParams>() {
- @Override
- public TaskParams createFromParcel(Parcel in) {
- return new TaskParams(in);
- }
-
- @Override
- public TaskParams[] newArray(int size) {
- return new TaskParams[size];
- }
- };
-}
diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java
deleted file mode 100644
index 8ce4484..0000000
--- a/core/java/android/app/task/TaskService.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.app.task;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p>
- * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
- * are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where
- * you will implement your task logic.</p>
- * <p>This service executes each incoming task on a {@link android.os.Handler} running on your
- * application's main thread. This means that you <b>must</b> offload your execution logic to
- * another thread/handler/{@link android.os.AsyncTask} of your choosing. Not doing so will result
- * in blocking any future callbacks from the TaskManager - specifically
- * {@link #onStopTask(android.app.task.TaskParams)}, which is meant to inform you that the
- * scheduling requirements are no longer being met.</p>
- */
-public abstract class TaskService extends Service {
- private static final String TAG = "TaskService";
-
- /**
- * Task services must be protected with this permission:
- *
- * <pre class="prettyprint">
- * <service android:name="MyTaskService"
- * android:permission="android.permission.BIND_TASK_SERVICE" >
- * ...
- * </service>
- * </pre>
- *
- * <p>If a task service is declared in the manifest but not protected with this
- * permission, that service will be ignored by the OS.
- */
- public static final String PERMISSION_BIND =
- "android.permission.BIND_TASK_SERVICE";
-
- /**
- * Identifier for a message that will result in a call to
- * {@link #onStartTask(android.app.task.TaskParams)}.
- */
- private final int MSG_EXECUTE_TASK = 0;
- /**
- * Message that will result in a call to {@link #onStopTask(android.app.task.TaskParams)}.
- */
- private final int MSG_STOP_TASK = 1;
- /**
- * Message that the client has completed execution of this task.
- */
- private final int MSG_TASK_FINISHED = 2;
-
- /** Lock object for {@link #mHandler}. */
- private final Object mHandlerLock = new Object();
-
- /**
- * Handler we post tasks to. Responsible for calling into the client logic, and handling the
- * callback to the system.
- */
- @GuardedBy("mHandlerLock")
- TaskHandler mHandler;
-
- /** Binder for this service. */
- ITaskService mBinder = new ITaskService.Stub() {
- @Override
- public void startTask(TaskParams taskParams) {
- ensureHandler();
- Message m = Message.obtain(mHandler, MSG_EXECUTE_TASK, taskParams);
- m.sendToTarget();
- }
- @Override
- public void stopTask(TaskParams taskParams) {
- ensureHandler();
- Message m = Message.obtain(mHandler, MSG_STOP_TASK, taskParams);
- m.sendToTarget();
- }
- };
-
- /** @hide */
- void ensureHandler() {
- synchronized (mHandlerLock) {
- if (mHandler == null) {
- mHandler = new TaskHandler(getMainLooper());
- }
- }
- }
-
- /**
- * Runs on application's main thread - callbacks are meant to offboard work to some other
- * (app-specified) mechanism.
- * @hide
- */
- class TaskHandler extends Handler {
- TaskHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- final TaskParams params = (TaskParams) msg.obj;
- switch (msg.what) {
- case MSG_EXECUTE_TASK:
- try {
- boolean workOngoing = TaskService.this.onStartTask(params);
- ackStartMessage(params, workOngoing);
- } catch (Exception e) {
- Log.e(TAG, "Error while executing task: " + params.getTaskId());
- throw new RuntimeException(e);
- }
- break;
- case MSG_STOP_TASK:
- try {
- boolean ret = TaskService.this.onStopTask(params);
- ackStopMessage(params, ret);
- } catch (Exception e) {
- Log.e(TAG, "Application unable to handle onStopTask.", e);
- throw new RuntimeException(e);
- }
- break;
- case MSG_TASK_FINISHED:
- final boolean needsReschedule = (msg.arg2 == 1);
- ITaskCallback callback = params.getCallback();
- if (callback != null) {
- try {
- callback.taskFinished(params.getTaskId(), needsReschedule);
- } catch (RemoteException e) {
- Log.e(TAG, "Error reporting task finish to system: binder has gone" +
- "away.");
- }
- } else {
- Log.e(TAG, "finishTask() called for a nonexistent task id.");
- }
- break;
- default:
- Log.e(TAG, "Unrecognised message received.");
- break;
- }
- }
-
- private void ackStartMessage(TaskParams params, boolean workOngoing) {
- final ITaskCallback callback = params.getCallback();
- final int taskId = params.getTaskId();
- if (callback != null) {
- try {
- callback.acknowledgeStartMessage(taskId, workOngoing);
- } catch(RemoteException e) {
- Log.e(TAG, "System unreachable for starting task.");
- }
- } else {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Attempting to ack a task that has already been processed.");
- }
- }
- }
-
- private void ackStopMessage(TaskParams params, boolean reschedule) {
- final ITaskCallback callback = params.getCallback();
- final int taskId = params.getTaskId();
- if (callback != null) {
- try {
- callback.acknowledgeStopMessage(taskId, reschedule);
- } catch(RemoteException e) {
- Log.e(TAG, "System unreachable for stopping task.");
- }
- } else {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Attempting to ack a task that has already been processed.");
- }
- }
- }
- }
-
- /** @hide */
- public final IBinder onBind(Intent intent) {
- return mBinder.asBinder();
- }
-
- /**
- * Override this method with the callback logic for your task. Any such logic needs to be
- * performed on a separate thread, as this function is executed on your application's main
- * thread.
- *
- * @param params Parameters specifying info about this task, including the extras bundle you
- * optionally provided at task-creation time.
- * @return True if your service needs to process the work (on a separate thread). False if
- * there's no more work to be done for this task.
- */
- public abstract boolean onStartTask(TaskParams params);
-
- /**
- * This method is called if the system has determined that you must stop execution of your task
- * even before you've had a chance to call {@link #taskFinished(TaskParams, boolean)}.
- *
- * <p>This will happen if the requirements specified at schedule time are no longer met. For
- * example you may have requested WiFi with
- * {@link android.app.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
- * task was executing the user toggled WiFi. Another example is if you had specified
- * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
- * idle maintenance window. You are solely responsible for the behaviour of your application
- * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
- * immediate repercussion is that the system will cease holding a wakelock for you.</p>
- *
- * @param params Parameters specifying info about this task.
- * @return True to indicate to the TaskManager whether you'd like to reschedule this task based
- * on the retry criteria provided at task creation-time. False to drop the task. Regardless of
- * the value returned, your task must stop executing.
- */
- public abstract boolean onStopTask(TaskParams params);
-
- /**
- * Callback to inform the TaskManager you've finished executing. This can be called from any
- * thread, as it will ultimately be run on your application's main thread. When the system
- * receives this message it will release the wakelock being held.
- * <p>
- * You can specify post-execution behaviour to the scheduler here with
- * <code>needsReschedule </code>. This will apply a back-off timer to your task based on
- * the default, or what was set with
- * {@link android.app.task.Task.Builder#setBackoffCriteria(long, int)}. The original
- * requirements are always honoured even for a backed-off task. Note that a task running in
- * idle mode will not be backed-off. Instead what will happen is the task will be re-added
- * to the queue and re-executed within a future idle maintenance window.
- * </p>
- *
- * @param params Parameters specifying system-provided info about this task, this was given to
- * your application in {@link #onStartTask(TaskParams)}.
- * @param needsReschedule True if this task is complete, false if you want the TaskManager to
- * reschedule you.
- */
- public final void taskFinished(TaskParams params, boolean needsReschedule) {
- ensureHandler();
- Message m = Message.obtain(mHandler, MSG_TASK_FINISHED, params);
- m.arg2 = needsReschedule ? 1 : 0;
- m.sendToTarget();
- }
-}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 7f8d0ab..64d80a0 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -874,6 +874,26 @@
}
/**
+ * Returns whether there is an open connection to this device.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+ *
+ * @return True if there is at least one open connection to this device.
+ * @hide
+ */
+ public boolean isConnected() {
+ if (sService == null) {
+ // BT is not enabled, we cannot be connected.
+ return false;
+ }
+ try {
+ return sService.isConnected(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+ }
+
+ /**
* Get the Bluetooth class of the remote device.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
*
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 07db8cc..a45c6b8 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -58,6 +58,7 @@
boolean cancelBondProcess(in BluetoothDevice device);
boolean removeBond(in BluetoothDevice device);
int getBondState(in BluetoothDevice device);
+ boolean isConnected(in BluetoothDevice device);
String getRemoteName(in BluetoothDevice device);
int getRemoteType(in BluetoothDevice device);
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 00a0750..273d76d 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -35,8 +35,6 @@
void startScan(in int appIf, in boolean isServer);
void startScanWithUuids(in int appIf, in boolean isServer, in ParcelUuid[] ids);
- void startScanWithUuidsScanParam(in int appIf, in boolean isServer,
- in ParcelUuid[] ids, int scanWindow, int scanInterval);
void startScanWithFilters(in int appIf, in boolean isServer,
in ScanSettings settings, in List<ScanFilter> filters);
void stopScan(in int appIf, in boolean isServer);
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index bf9e0a7..2d8eed4 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -22,7 +22,7 @@
* Callback definitions for interacting with BLE / GATT
* @hide
*/
-interface IBluetoothGattCallback {
+oneway interface IBluetoothGattCallback {
void onClientRegistered(in int status, in int clientIf);
void onClientConnectionState(in int status, in int clientIf,
in boolean connected, in String address);
@@ -63,7 +63,7 @@
in int charInstId, in ParcelUuid charUuid,
in byte[] value);
void onReadRemoteRssi(in String address, in int rssi, in int status);
- oneway void onAdvertiseStateChange(in int advertiseState, in int status);
- oneway void onMultiAdvertiseCallback(in int status);
+ void onAdvertiseStateChange(in int advertiseState, in int status);
+ void onMultiAdvertiseCallback(in int status);
void onConfigureMTU(in String address, in int mtu, in int status);
}
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7642e13..392bfbc 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1839,19 +1839,6 @@
}
/**
- * Cancel any active or pending syncs that are running on this service.
- *
- * @param cname the service for which to cancel all active/pending operations.
- */
- public static void cancelSync(ComponentName cname) {
- try {
- getContentService().cancelSync(null, null, cname);
- } catch (RemoteException e) {
-
- }
- }
-
- /**
* Get information about the SyncAdapters that are known to the system.
* @return an array of SyncAdapters that have registered with the system
*/
@@ -1991,13 +1978,13 @@
/**
* Remove the specified sync. This will cancel any pending or active syncs. If the request is
* for a periodic sync, this call will remove any future occurrences.
- * <p>If a periodic sync is specified, the caller must hold the permission
- * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}. If this SyncRequest targets a
- * SyncService adapter,the calling application must be signed with the same certificate as the
- * adapter.
- *</p>It is possible to cancel a sync using a SyncRequest object that is not the same object
+ * <p>
+ * If a periodic sync is specified, the caller must hold the permission
+ * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
+ *</p>
+ * It is possible to cancel a sync using a SyncRequest object that is not the same object
* with which you requested the sync. Do so by building a SyncRequest with the same
- * service/adapter, frequency, <b>and</b> extras bundle.
+ * adapter, frequency, <b>and</b> extras bundle.
*
* @param request SyncRequest object containing information about sync to cancel.
*/
@@ -2031,22 +2018,6 @@
}
/**
- * Return periodic syncs associated with the provided component.
- * <p>The calling application must be signed with the same certificate as the target component,
- * otherwise this call will fail.
- */
- public static List<PeriodicSync> getPeriodicSyncs(ComponentName cname) {
- if (cname == null) {
- throw new IllegalArgumentException("Component must not be null");
- }
- try {
- return getContentService().getPeriodicSyncs(null, null, cname);
- } catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
- }
- }
-
- /**
* Check if this account/provider is syncable.
* <p>This method requires the caller to hold the permission
* {@link android.Manifest.permission#READ_SYNC_SETTINGS}.
@@ -2076,38 +2047,6 @@
}
/**
- * Set whether the provided {@link SyncService} is available to process work.
- * <p>This method requires the caller to hold the permission
- * {@link android.Manifest.permission#WRITE_SYNC_SETTINGS}.
- * <p>The calling application must be signed with the same certificate as the target component,
- * otherwise this call will fail.
- */
- public static void setServiceActive(ComponentName cname, boolean active) {
- try {
- getContentService().setServiceActive(cname, active);
- } catch (RemoteException e) {
- // exception ignored; if this is thrown then it means the runtime is in the midst of
- // being restarted
- }
- }
-
- /**
- * Query the state of this sync service.
- * <p>Set with {@link #setServiceActive(ComponentName cname, boolean active)}.
- * <p>The calling application must be signed with the same certificate as the target component,
- * otherwise this call will fail.
- * @param cname ComponentName referring to a {@link SyncService}
- * @return true if jobs will be run on this service, false otherwise.
- */
- public static boolean isServiceActive(ComponentName cname) {
- try {
- return getContentService().isServiceActive(cname);
- } catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
- }
- }
-
- /**
* Gets the master auto-sync setting that applies to all the providers and accounts.
* If this is false then the per-provider auto-sync setting is ignored.
* <p>This method requires the caller to hold the permission
@@ -2164,17 +2103,6 @@
}
}
- public static boolean isSyncActive(ComponentName cname) {
- if (cname == null) {
- throw new IllegalArgumentException("component name must not be null");
- }
- try {
- return getContentService().isSyncActive(null, null, cname);
- } catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
- }
- }
-
/**
* If a sync is active returns the information about it, otherwise returns null.
* <p>
@@ -2249,14 +2177,6 @@
}
}
- public static boolean isSyncPending(ComponentName cname) {
- try {
- return getContentService().isSyncPending(null, null, cname);
- } catch (RemoteException e) {
- throw new RuntimeException("the ContentService should always be reachable", e);
- }
- }
-
/**
* Request notifications when the different aspects of the SyncManager change. The
* different items that can be requested are:
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c69e669..ccf8451 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@@ -2058,7 +2059,7 @@
PRINT_SERVICE,
MEDIA_SESSION_SERVICE,
BATTERY_SERVICE,
- TASK_SERVICE,
+ JOB_SCHEDULER_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -2115,8 +2116,8 @@
* <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
* <dt> {@link #BATTERY_SERVICE} ("batterymanager")
* <dd> A {@link android.os.BatteryManager} for managing battery state
- * <dt> {@link #TASK_SERVICE} ("taskmanager")
- * <dd> A {@link android.app.task.TaskManager} for managing scheduled tasks
+ * <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager")
+ * <dd> A {@link android.app.job.JobScheduler} for managing scheduled tasks
* </dl>
*
* <p>Note: System services obtained via this API may be closely associated with
@@ -2148,8 +2149,6 @@
* @see android.app.SearchManager
* @see #SENSOR_SERVICE
* @see android.hardware.SensorManager
- * @see #HDMI_CEC_SERVICE
- * @see android.hardware.hdmi.HdmiCecManager
* @see #STORAGE_SERVICE
* @see android.os.storage.StorageManager
* @see #VIBRATOR_SERVICE
@@ -2172,8 +2171,8 @@
* @see android.app.DownloadManager
* @see #BATTERY_SERVICE
* @see android.os.BatteryManager
- * @see #TASK_SERVICE
- * @see android.app.task.TaskManager
+ * @see #JOB_SCHEDULER_SERVICE
+ * @see android.app.job.JobScheduler
*/
public abstract Object getSystemService(@ServiceName @NonNull String name);
@@ -2637,23 +2636,14 @@
/**
* Use with {@link #getSystemService} to retrieve a
- * {@link android.hardware.hdmi.HdmiCecManager} for controlling and managing
- * HDMI-CEC protocol.
- *
- * @see #getSystemService
- * @see android.hardware.hdmi.HdmiCecManager
- */
- // TODO: Remove this once HdmiControlService is ready.
- public static final String HDMI_CEC_SERVICE = "hdmi_cec";
-
- /**
- * Use with {@link #getSystemService} to retrieve a
* {@link android.hardware.hdmi.HdmiControlManager} for controlling and managing
* HDMI-CEC protocol.
*
* @see #getSystemService
* @see android.hardware.hdmi.HdmiControlManager
+ * @hide
*/
+ @SystemApi
public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
/**
@@ -2751,11 +2741,11 @@
/**
* Use with {@link #getSystemService} to retrieve a
- * {@link android.tv.TvInputManager} for interacting with TV inputs on the
- * device.
+ * {@link android.media.tv.TvInputManager} for interacting with TV inputs
+ * on the device.
*
* @see #getSystemService
- * @see android.tv.TvInputManager
+ * @see android.media.tv.TvInputManager
*/
public static final String TV_INPUT_SERVICE = "tv_input";
@@ -2779,12 +2769,12 @@
/**
* Use with {@link #getSystemService} to retrieve a {@link
- * android.app.task.TaskManager} instance for managing occasional
+ * android.app.job.JobScheduler} instance for managing occasional
* background tasks.
* @see #getSystemService
- * @see android.app.task.TaskManager
+ * @see android.app.job.JobScheduler
*/
- public static final String TASK_SERVICE = "task";
+ public static final String JOB_SCHEDULER_SERVICE = "jobscheduler";
/**
* Determine whether the given permission is allowed for a particular
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 73a76e8..373f2fb 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -121,19 +121,6 @@
*/
void setIsSyncable(in Account account, String providerName, int syncable);
- /**
- * Corresponds roughly to setIsSyncable(String account, String provider) for syncs that bind
- * to a SyncService.
- */
- void setServiceActive(in ComponentName cname, boolean active);
-
- /**
- * Corresponds roughly to getIsSyncable(String account, String provider) for syncs that bind
- * to a SyncService.
- * @return 0 if this SyncService is not enabled, 1 if enabled, <0 if unknown.
- */
- boolean isServiceActive(in ComponentName cname);
-
void setMasterSyncAutomatically(boolean flag);
boolean getMasterSyncAutomatically();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bd07470..6e53a6fb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2656,7 +2656,9 @@
/**
* Broadcast sent to the primary user when an associated managed profile is added (the profile
* was created and is ready to be used). Carries an extra {@link #EXTRA_USER} that specifies
- * the UserHandle of the profile that was added. This is only sent to registered receivers,
+ * the UserHandle of the profile that was added. Only applications (for example Launchers)
+ * that need to display merged content across both primary and managed profiles need to
+ * worry about this broadcast. This is only sent to registered receivers,
* not manifest receivers.
*/
public static final String ACTION_MANAGED_PROFILE_ADDED =
@@ -2664,8 +2666,10 @@
/**
* Broadcast sent to the primary user when an associated managed profile is removed. Carries an
- * extra {@link #EXTRA_USER} that specifies the UserHandle of the profile that was removed. This
- * is only sent to registered receivers, not manifest receivers.
+ * extra {@link #EXTRA_USER} that specifies the UserHandle of the profile that was removed.
+ * Only applications (for example Launchers) that need to display merged content across both
+ * primary and managed profiles need to worry about this broadcast. This is only sent to
+ * registered receivers, not manifest receivers.
*/
public static final String ACTION_MANAGED_PROFILE_REMOVED =
"android.intent.action.MANAGED_PROFILE_REMOVED";
@@ -2731,6 +2735,7 @@
* returned in {@link #getClipData()}.
*
* @see DocumentsContract
+ * @see #ACTION_OPEN_DOCUMENT_TREE
* @see #ACTION_CREATE_DOCUMENT
* @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
*/
@@ -2765,28 +2770,30 @@
*
* @see DocumentsContract
* @see #ACTION_OPEN_DOCUMENT
+ * @see #ACTION_OPEN_DOCUMENT_TREE
* @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
/**
- * Activity Action: Allow the user to pick a directory. When invoked, the
- * system will display the various {@link DocumentsProvider} instances
- * installed on the device, letting the user navigate through them. Apps can
- * fully manage documents within the returned directory.
+ * Activity Action: Allow the user to pick a directory subtree. When
+ * invoked, the system will display the various {@link DocumentsProvider}
+ * instances installed on the device, letting the user navigate through
+ * them. Apps can fully manage documents within the returned directory.
* <p>
* To gain access to descendant (child, grandchild, etc) documents, use
- * {@link DocumentsContract#buildDocumentViaUri(Uri, String)} and
- * {@link DocumentsContract#buildChildDocumentsViaUri(Uri, String)} using
- * the returned directory URI.
+ * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)} and
+ * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)}
+ * with the returned URI.
* <p>
- * Output: The URI representing the selected directory.
+ * Output: The URI representing the selected directory tree.
*
* @see DocumentsContract
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_PICK_DIRECTORY = "android.intent.action.PICK_DIRECTORY";
+ public static final String
+ ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
@@ -2976,14 +2983,6 @@
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE";
- /**
- * An activity that provides a user interface for adjusting notification preferences for its
- * containing application. Optional but recommended for apps that post
- * {@link android.app.Notification Notifications}.
- */
- @SdkConstant(SdkConstantType.INTENT_CATEGORY)
- public static final String CATEGORY_NOTIFICATION_PREFERENCES = "android.intent.category.NOTIFICATION_PREFERENCES";
-
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Application launch intent categories (see addCategory()).
@@ -3373,8 +3372,8 @@
*
* @see #ACTION_GET_CONTENT
* @see #ACTION_OPEN_DOCUMENT
+ * @see #ACTION_OPEN_DOCUMENT_TREE
* @see #ACTION_CREATE_DOCUMENT
- * @see #ACTION_PICK_DIRECTORY
*/
public static final String EXTRA_LOCAL_ONLY =
"android.intent.extra.LOCAL_ONLY";
@@ -3712,30 +3711,8 @@
*/
public static final int FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY = 0x00100000;
/**
- * If set, this marks a point in the task's activity stack that should
- * be cleared when the task is reset. That is, the next time the task
- * is brought to the foreground with
- * {@link #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED} (typically as a result of
- * the user re-launching it from home), this activity and all on top of
- * it will be finished so that the user does not return to them, but
- * instead returns to whatever activity preceeded it.
- *
- * <p>When this flag is assigned to the root activity all activities up
- * to, but not including the root activity, will be cleared. This prevents
- * this flag from being used to finish all activities in a task and thereby
- * ending the task.
- *
- * <p>This is useful for cases where you have a logical break in your
- * application. For example, an e-mail application may have a command
- * to view an attachment, which launches an image view activity to
- * display it. This activity should be part of the e-mail application's
- * task, since it is a part of the task the user is involved in. However,
- * if the user leaves that task, and later selects the e-mail app from
- * home, we may like them to return to the conversation they were
- * viewing, not the picture attachment, since that is confusing. By
- * setting this flag when launching the image viewer, that viewer and
- * any activities it starts will be removed the next time the user returns
- * to mail.
+ * @deprecated As of API 21 this performs identically to
+ * {@link #FLAG_ACTIVITY_NEW_DOCUMENT} which should be used instead of this.
*/
public static final int FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET = 0x00080000;
/**
@@ -3762,8 +3739,7 @@
* @see android.R.attr#documentLaunchMode
* @see #FLAG_ACTIVITY_MULTIPLE_TASK
*/
- public static final int FLAG_ACTIVITY_NEW_DOCUMENT =
- FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | FLAG_ACTIVITY_NEW_TASK;
+ public static final int FLAG_ACTIVITY_NEW_DOCUMENT = FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
/**
* If set, this flag will prevent the normal {@link android.app.Activity#onUserLeaveHint}
* callback from occurring on the current frontmost activity before it is
diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java
index 836c6f8..3efd89a 100644
--- a/core/java/android/content/PeriodicSync.java
+++ b/core/java/android/content/PeriodicSync.java
@@ -29,14 +29,10 @@
public final Account account;
/** The authority of the sync. Can be null. */
public final String authority;
- /** The service for syncing, if this is an anonymous sync. Can be null.*/
- public final ComponentName service;
/** Any extras that parameters that are to be passed to the sync adapter. */
public final Bundle extras;
/** How frequently the sync should be scheduled, in seconds. Kept around for API purposes. */
public final long period;
- /** Whether this periodic sync runs on a {@link SyncService}. */
- public final boolean isService;
/**
* How much flexibility can be taken in scheduling the sync, in seconds.
* {@hide}
@@ -44,16 +40,11 @@
public final long flexTime;
/**
- * Creates a new PeriodicSync, copying the Bundle. SM no longer uses this ctor - kept around
- * becuse it is part of the API.
- * Note - even calls to the old API will not use this ctor, as
- * they are given a default flex time.
+ * Creates a new PeriodicSync, copying the Bundle. This constructor is no longer used.
*/
public PeriodicSync(Account account, String authority, Bundle extras, long periodInSeconds) {
this.account = account;
this.authority = authority;
- this.service = null;
- this.isService = false;
if (extras == null) {
this.extras = new Bundle();
} else {
@@ -71,8 +62,6 @@
public PeriodicSync(PeriodicSync other) {
this.account = other.account;
this.authority = other.authority;
- this.service = other.service;
- this.isService = other.isService;
this.extras = new Bundle(other.extras);
this.period = other.period;
this.flexTime = other.flexTime;
@@ -86,40 +75,14 @@
long period, long flexTime) {
this.account = account;
this.authority = authority;
- this.service = null;
- this.isService = false;
- this.extras = new Bundle(extras);
- this.period = period;
- this.flexTime = flexTime;
- }
-
- /**
- * A PeriodicSync for a sync with a specified SyncService.
- * {@hide}
- */
- public PeriodicSync(ComponentName service, Bundle extras,
- long period,
- long flexTime) {
- this.account = null;
- this.authority = null;
- this.service = service;
- this.isService = true;
this.extras = new Bundle(extras);
this.period = period;
this.flexTime = flexTime;
}
private PeriodicSync(Parcel in) {
- this.isService = (in.readInt() != 0);
- if (this.isService) {
- this.service = in.readParcelable(null);
- this.account = null;
- this.authority = null;
- } else {
- this.account = in.readParcelable(null);
- this.authority = in.readString();
- this.service = null;
- }
+ this.account = in.readParcelable(null);
+ this.authority = in.readString();
this.extras = in.readBundle();
this.period = in.readLong();
this.flexTime = in.readLong();
@@ -132,13 +95,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(isService ? 1 : 0);
- if (account == null && authority == null) {
- dest.writeParcelable(service, flags);
- } else {
- dest.writeParcelable(account, flags);
- dest.writeString(authority);
- }
+ dest.writeParcelable(account, flags);
+ dest.writeString(authority);
dest.writeBundle(extras);
dest.writeLong(period);
dest.writeLong(flexTime);
@@ -165,24 +123,14 @@
return false;
}
final PeriodicSync other = (PeriodicSync) o;
- if (this.isService != other.isService) {
- return false;
- }
- boolean equal = false;
- if (this.isService) {
- equal = service.equals(other.service);
- } else {
- equal = account.equals(other.account)
- && authority.equals(other.authority);
- }
- return equal
- && period == other.period
- && syncExtrasEquals(extras, other.extras);
+ return account.equals(other.account)
+ && authority.equals(other.authority)
+ && period == other.period
+ && syncExtrasEquals(extras, other.extras);
}
/**
- * Periodic sync extra comparison function. Duplicated from
- * {@link com.android.server.content.SyncManager#syncExtrasEquals(Bundle b1, Bundle b2)}
+ * Periodic sync extra comparison function.
* {@hide}
*/
public static boolean syncExtrasEquals(Bundle b1, Bundle b2) {
@@ -207,7 +155,6 @@
public String toString() {
return "account: " + account +
", authority: " + authority +
- ", service: " + service +
". period: " + period + "s " +
", flex: " + flexTime;
}
diff --git a/core/java/android/content/SyncInfo.java b/core/java/android/content/SyncInfo.java
index 146dd99..a586d6f 100644
--- a/core/java/android/content/SyncInfo.java
+++ b/core/java/android/content/SyncInfo.java
@@ -28,24 +28,16 @@
public final int authorityId;
/**
- * The {@link Account} that is currently being synced. Will be null if this sync is running via
- * a {@link SyncService}.
+ * The {@link Account} that is currently being synced.
*/
public final Account account;
/**
- * The authority of the provider that is currently being synced. Will be null if this sync
- * is running via a {@link SyncService}.
+ * The authority of the provider that is currently being synced.
*/
public final String authority;
/**
- * The {@link SyncService} that is targeted by this operation. Null if this sync is running via
- * a {@link AbstractThreadedSyncAdapter}.
- */
- public final ComponentName service;
-
- /**
* The start time of the current sync operation in milliseconds since boot.
* This is represented in elapsed real time.
* See {@link android.os.SystemClock#elapsedRealtime()}.
@@ -53,13 +45,11 @@
public final long startTime;
/** @hide */
- public SyncInfo(int authorityId, Account account, String authority, ComponentName service,
- long startTime) {
+ public SyncInfo(int authorityId, Account account, String authority, long startTime) {
this.authorityId = authorityId;
this.account = account;
this.authority = authority;
this.startTime = startTime;
- this.service = service;
}
/** @hide */
@@ -68,7 +58,6 @@
this.account = new Account(other.account.name, other.account.type);
this.authority = other.authority;
this.startTime = other.startTime;
- this.service = other.service;
}
/** @hide */
@@ -82,8 +71,6 @@
parcel.writeParcelable(account, flags);
parcel.writeString(authority);
parcel.writeLong(startTime);
- parcel.writeParcelable(service, flags);
-
}
/** @hide */
@@ -92,7 +79,6 @@
account = parcel.readParcelable(Account.class.getClassLoader());
authority = parcel.readString();
startTime = parcel.readLong();
- service = parcel.readParcelable(ComponentName.class.getClassLoader());
}
/** @hide */
diff --git a/core/java/android/content/SyncRequest.java b/core/java/android/content/SyncRequest.java
index 9ba45ca..7619c6d 100644
--- a/core/java/android/content/SyncRequest.java
+++ b/core/java/android/content/SyncRequest.java
@@ -21,29 +21,22 @@
import android.os.Parcel;
import android.os.Parcelable;
+/**
+ * Convenience class to construct sync requests. See {@link android.content.SyncRequest.Builder}
+ * for an explanation of the various functions. The resulting object is passed through to the
+ * framework via {@link android.content.ContentResolver#requestSync(SyncRequest)}.
+ */
public class SyncRequest implements Parcelable {
private static final String TAG = "SyncRequest";
/** Account to pass to the sync adapter. Can be null. */
private final Account mAccountToSync;
/** Authority string that corresponds to a ContentProvider. */
private final String mAuthority;
- /** {@link SyncService} identifier. */
- private final ComponentName mComponentInfo;
/** Bundle containing user info as well as sync settings. */
private final Bundle mExtras;
/** Don't allow this sync request on metered networks. */
private final boolean mDisallowMetered;
/**
- * Anticipated upload size in bytes.
- * TODO: Not yet used - we put this information into the bundle for simplicity.
- */
- private final long mTxBytes;
- /**
- * Anticipated download size in bytes.
- * TODO: Not yet used - we put this information into the bundle.
- */
- private final long mRxBytes;
- /**
* Amount of time before {@link #mSyncRunTimeSecs} from which the sync may optionally be
* started.
*/
@@ -69,17 +62,12 @@
return mIsPeriodic;
}
- public boolean isExpedited() {
- return mIsExpedited;
- }
-
/**
* {@hide}
- * @return true if this sync uses an account/authority pair, or false if
- * this is an anonymous sync bound to an @link AnonymousSyncService.
+ * @return whether this sync is expedited.
*/
- public boolean hasAuthority() {
- return mIsAuthority;
+ public boolean isExpedited() {
+ return mIsExpedited;
}
/**
@@ -90,10 +78,6 @@
* sync service.
*/
public Account getAccount() {
- if (!hasAuthority()) {
- throw new IllegalArgumentException("Cannot getAccount() for a sync that targets a sync"
- + "service.");
- }
return mAccountToSync;
}
@@ -105,30 +89,11 @@
* sync service.
*/
public String getProvider() {
- if (!hasAuthority()) {
- throw new IllegalArgumentException("Cannot getProvider() for a sync that targets a"
- + "sync service.");
- }
return mAuthority;
}
/**
* {@hide}
- * Throws a runtime IllegalArgumentException if this function is called for a
- * SyncRequest that is bound to an account/provider.
- *
- * @return ComponentName for the service that this sync will bind to.
- */
- public ComponentName getService() {
- if (hasAuthority()) {
- throw new IllegalArgumentException(
- "Cannot getAnonymousService() for a sync that has specified a provider.");
- }
- return mComponentInfo;
- }
-
- /**
- * {@hide}
* Retrieve bundle for this SyncRequest. Will not be null.
*/
public Bundle getBundle() {
@@ -175,16 +140,10 @@
parcel.writeLong(mSyncRunTimeSecs);
parcel.writeInt((mIsPeriodic ? 1 : 0));
parcel.writeInt((mDisallowMetered ? 1 : 0));
- parcel.writeLong(mTxBytes);
- parcel.writeLong(mRxBytes);
parcel.writeInt((mIsAuthority ? 1 : 0));
parcel.writeInt((mIsExpedited? 1 : 0));
- if (mIsAuthority) {
- parcel.writeParcelable(mAccountToSync, flags);
- parcel.writeString(mAuthority);
- } else {
- parcel.writeParcelable(mComponentInfo, flags);
- }
+ parcel.writeParcelable(mAccountToSync, flags);
+ parcel.writeString(mAuthority);
}
private SyncRequest(Parcel in) {
@@ -193,19 +152,10 @@
mSyncRunTimeSecs = in.readLong();
mIsPeriodic = (in.readInt() != 0);
mDisallowMetered = (in.readInt() != 0);
- mTxBytes = in.readLong();
- mRxBytes = in.readLong();
mIsAuthority = (in.readInt() != 0);
mIsExpedited = (in.readInt() != 0);
- if (mIsAuthority) {
- mComponentInfo = null;
- mAccountToSync = in.readParcelable(null);
- mAuthority = in.readString();
- } else {
- mComponentInfo = in.readParcelable(null);
- mAccountToSync = null;
- mAuthority = null;
- }
+ mAccountToSync = in.readParcelable(null);
+ mAuthority = in.readString();
}
/** {@hide} Protected ctor to instantiate anonymous SyncRequest. */
@@ -214,7 +164,6 @@
mSyncRunTimeSecs = b.mSyncRunTimeSecs;
mAccountToSync = b.mAccount;
mAuthority = b.mAuthority;
- mComponentInfo = b.mComponentName;
mIsPeriodic = (b.mSyncType == Builder.SYNC_TYPE_PERIODIC);
mIsAuthority = (b.mSyncTarget == Builder.SYNC_TARGET_ADAPTER);
mIsExpedited = b.mExpedited;
@@ -223,8 +172,6 @@
// TODO: pass the configuration extras through separately.
mExtras.putAll(b.mSyncConfigExtras);
mDisallowMetered = b.mDisallowMetered;
- mTxBytes = b.mTxBytes;
- mRxBytes = b.mRxBytes;
}
/**
@@ -240,8 +187,6 @@
private static final int SYNC_TYPE_ONCE = 2;
/** Unknown sync target. */
private static final int SYNC_TARGET_UNKNOWN = 0;
- /** Specify that this is an anonymous sync. */
- private static final int SYNC_TARGET_SERVICE = 1;
/** Specify that this is a sync with a provider. */
private static final int SYNC_TARGET_ADAPTER = 2;
/**
@@ -263,19 +208,13 @@
* discriminate between equivalent syncs.
*/
private Bundle mSyncConfigExtras;
- /** Expected upload transfer in bytes. */
- private long mTxBytes = -1L;
- /** Expected download transfer in bytes. */
- private long mRxBytes = -1L;
/** Whether or not this sync can occur on metered networks. Default false. */
private boolean mDisallowMetered;
- /** Priority of this sync relative to others from calling app [-2, 2]. Default 0. */
- private int mPriority = 0;
/**
* Whether this builder is building a periodic sync, or a one-time sync.
*/
private int mSyncType = SYNC_TYPE_UNKNOWN;
- /** Whether this will go to a sync adapter or to a sync service. */
+ /** Whether this will go to a sync adapter. */
private int mSyncTarget = SYNC_TARGET_UNKNOWN;
/** Whether this is a user-activated sync. */
private boolean mIsManual;
@@ -298,12 +237,6 @@
private boolean mExpedited;
/**
- * The {@link SyncService} component that
- * contains the sync logic if this is a provider-less sync, otherwise
- * null.
- */
- private ComponentName mComponentName;
- /**
* The Account object that together with an Authority name define the SyncAdapter (if
* this sync is bound to a provider), otherwise null.
*/
@@ -336,7 +269,7 @@
/**
* Build a periodic sync. Either this or syncOnce() <b>must</b> be called for this builder.
- * Syncs are identified by target {@link SyncService}/{@link android.provider} and by the
+ * Syncs are identified by target {@link android.provider} and by the
* contents of the extras bundle.
* You cannot reuse the same builder for one-time syncs after having specified a periodic
* sync (by calling this function). If you do, an <code>IllegalArgumentException</code>
@@ -384,7 +317,6 @@
return this;
}
- /** {@hide} */
private void setupInterval(long at, long before) {
if (before > at) {
throw new IllegalArgumentException("Specified run time for the sync must be" +
@@ -395,23 +327,10 @@
}
/**
- * Developer can provide insight into their payload size; optional. -1 specifies unknown,
- * so that you are not restricted to defining both fields.
- *
- * @param rxBytes Bytes expected to be downloaded.
- * @param txBytes Bytes expected to be uploaded.
- */
- public Builder setTransferSize(long rxBytes, long txBytes) {
- mRxBytes = rxBytes;
- mTxBytes = txBytes;
- return this;
- }
-
- /**
* Will throw an <code>IllegalArgumentException</code> if called and
* {@link #setIgnoreSettings(boolean ignoreSettings)} has already been called.
* @param disallow true to allow this transfer on metered networks. Default false.
- *
+ *
*/
public Builder setDisallowMetered(boolean disallow) {
if (mIgnoreSettings && disallow) {
@@ -423,10 +342,9 @@
}
/**
- * Specify an authority and account for this transfer. Cannot be used with
- * {@link #setSyncAdapter(ComponentName cname)}.
+ * Specify an authority and account for this transfer.
*
- * @param authority
+ * @param authority A String identifying the content provider to be synced.
* @param account Account to sync. Can be null unless this is a periodic
* sync, for which verification by the ContentResolver will
* fail. If a sync is performed without an account, the
@@ -441,25 +359,6 @@
mSyncTarget = SYNC_TARGET_ADAPTER;
mAccount = account;
mAuthority = authority;
- mComponentName = null;
- return this;
- }
-
- /**
- * Specify the {@link SyncService} component for this sync. This is not validated until
- * sync time so providing an incorrect component name here will not fail. Cannot be used
- * with {@link #setSyncAdapter(Account account, String authority)}.
- *
- * @param cname ComponentName to identify your Anonymous service
- */
- public Builder setSyncAdapter(ComponentName cname) {
- if (mSyncTarget != SYNC_TARGET_UNKNOWN) {
- throw new IllegalArgumentException("Sync target has already been defined.");
- }
- mSyncTarget = SYNC_TARGET_SERVICE;
- mComponentName = cname;
- mAccount = null;
- mAuthority = null;
return this;
}
@@ -580,18 +479,6 @@
}
/**
- * @param priority the priority of this request among all requests from the calling app.
- * Range of [-2,2] similar to how this is done with notifications.
- */
- public Builder setPriority(int priority) {
- if (priority < -2 || priority > 2) {
- throw new IllegalArgumentException("Priority must be within range [-2, 2]");
- }
- mPriority = priority;
- return this;
- }
-
- /**
* Performs validation over the request and throws the runtime exception
* <code>IllegalArgumentException</code> if this validation fails.
*
@@ -625,30 +512,19 @@
mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
mSyncConfigExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
}
- mSyncConfigExtras.putLong(ContentResolver.SYNC_EXTRAS_EXPECTED_UPLOAD, mTxBytes);
- mSyncConfigExtras.putLong(ContentResolver.SYNC_EXTRAS_EXPECTED_DOWNLOAD, mRxBytes);
- mSyncConfigExtras.putInt(ContentResolver.SYNC_EXTRAS_PRIORITY, mPriority);
if (mSyncType == SYNC_TYPE_PERIODIC) {
// If this is a periodic sync ensure than invalid extras were not set.
- if (ContentResolver.invalidPeriodicExtras(mCustomExtras) ||
+ if (ContentResolver.invalidPeriodicExtras(mCustomExtras) ||
ContentResolver.invalidPeriodicExtras(mSyncConfigExtras)) {
throw new IllegalArgumentException("Illegal extras were set");
}
- } else if (mSyncType == SYNC_TYPE_UNKNOWN) {
- throw new IllegalArgumentException("Must call either syncOnce() or syncPeriodic()");
- }
- if (mSyncTarget == SYNC_TARGET_SERVICE) {
- if (mSyncConfigExtras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
- throw new IllegalArgumentException("Cannot specify an initialisation sync"
- + " that targets a service.");
- }
}
// Ensure that a target for the sync has been set.
if (mSyncTarget == SYNC_TARGET_UNKNOWN) {
- throw new IllegalArgumentException("Must specify an adapter with one of"
- + "setSyncAdapter(ComponentName) or setSyncAdapter(Account, String");
+ throw new IllegalArgumentException("Must specify an adapter with" +
+ " setSyncAdapter(Account, String");
}
return new SyncRequest(this);
}
- }
+ }
}
diff --git a/core/java/android/content/SyncService.java b/core/java/android/content/SyncService.java
deleted file mode 100644
index 4df998c..0000000
--- a/core/java/android/content/SyncService.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.app.Service;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.Trace;
-import android.util.SparseArray;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * Simplified @link android.content.AbstractThreadedSyncAdapter. Folds that
- * behaviour into a service to which the system can bind when requesting an
- * anonymous (providerless/accountless) sync.
- * <p>
- * In order to perform an anonymous sync operation you must extend this service, implementing the
- * abstract methods. This service must be declared in the application's manifest as usual. You
- * can use this service for other work, however you <b> must not </b> override the onBind() method
- * unless you know what you're doing, which limits the usefulness of this service for other work.
- * <p>A {@link SyncService} can either be active or inactive. Different to an
- * {@link AbstractThreadedSyncAdapter}, there is no
- * {@link ContentResolver#setSyncAutomatically(android.accounts.Account account, String provider, boolean sync)},
- * as well as no concept of initialisation (you can handle your own if needed).
- *
- * <pre>
- * <service android:name=".MySyncService"/>
- * </pre>
- * Like @link android.content.AbstractThreadedSyncAdapter this service supports
- * multiple syncs at the same time. Each incoming startSync() with a unique tag
- * will spawn a thread to do the work of that sync. If startSync() is called
- * with a tag that already exists, a SyncResult.ALREADY_IN_PROGRESS is returned.
- * Remember that your service will spawn multiple threads if you schedule multiple syncs
- * at once, so if you mutate local objects you must ensure synchronization.
- */
-public abstract class SyncService extends Service {
- private static final String TAG = "SyncService";
-
- private final SyncAdapterImpl mSyncAdapter = new SyncAdapterImpl();
-
- /** Keep track of on-going syncs, keyed by bundle. */
- @GuardedBy("mSyncThreadLock")
- private final SparseArray<SyncThread>
- mSyncThreads = new SparseArray<SyncThread>();
- /** Lock object for accessing the SyncThreads HashMap. */
- private final Object mSyncThreadLock = new Object();
- /**
- * Default key for if this sync service does not support parallel operations. Currently not
- * sure if null keys will make it into the ArrayMap for KLP, so keeping our default for now.
- */
- private static final int KEY_DEFAULT = 0;
- /** Identifier for this sync service. */
- private ComponentName mServiceComponent;
-
- /** {@hide} */
- public IBinder onBind(Intent intent) {
- mServiceComponent = new ComponentName(this, getClass());
- return mSyncAdapter.asBinder();
- }
-
- /** {@hide} */
- private class SyncAdapterImpl extends ISyncServiceAdapter.Stub {
- @Override
- public void startSync(ISyncContext syncContext, Bundle extras) {
- // Wrap the provided Sync Context because it may go away by the time
- // we call it.
- final SyncContext syncContextClient = new SyncContext(syncContext);
- boolean alreadyInProgress = false;
- final int extrasAsKey = extrasToKey(extras);
- synchronized (mSyncThreadLock) {
- if (mSyncThreads.get(extrasAsKey) == null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "starting sync for : " + mServiceComponent);
- }
- // Start sync.
- SyncThread syncThread = new SyncThread(syncContextClient, extras);
- mSyncThreads.put(extrasAsKey, syncThread);
- syncThread.start();
- } else {
- // Don't want to call back to SyncManager while still
- // holding lock.
- alreadyInProgress = true;
- }
- }
- if (alreadyInProgress) {
- syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
- }
- }
-
- /**
- * Used by the SM to cancel a specific sync using the
- * com.android.server.content.SyncManager.ActiveSyncContext as a handle.
- */
- @Override
- public void cancelSync(ISyncContext syncContext) {
- SyncThread runningSync = null;
- synchronized (mSyncThreadLock) {
- for (int i = 0; i < mSyncThreads.size(); i++) {
- SyncThread thread = mSyncThreads.valueAt(i);
- if (thread.mSyncContext.getSyncContextBinder() == syncContext.asBinder()) {
- runningSync = thread;
- break;
- }
- }
- }
- if (runningSync != null) {
- runningSync.interrupt();
- }
- }
- }
-
- /**
- *
- * @param extras Bundle for which to compute hash
- * @return an integer hash that is equal to that of another bundle if they both contain the
- * same key -> value mappings, however, not necessarily in order.
- * Based on the toString() representation of the value mapped.
- */
- private int extrasToKey(Bundle extras) {
- int hash = KEY_DEFAULT; // Empty bundle, or no parallel operations enabled.
- if (parallelSyncsEnabled()) {
- for (String key : extras.keySet()) {
- String mapping = key + " " + extras.get(key).toString();
- hash += mapping.hashCode();
- }
- }
- return hash;
- }
-
- /**
- * {@hide}
- * Similar to {@link android.content.AbstractThreadedSyncAdapter.SyncThread}. However while
- * the ATSA considers an already in-progress sync to be if the account provided is currently
- * syncing, this anonymous sync has no notion of account and considers a sync unique if the
- * provided bundle is different.
- */
- private class SyncThread extends Thread {
- private final SyncContext mSyncContext;
- private final Bundle mExtras;
- private final int mThreadsKey;
-
- public SyncThread(SyncContext syncContext, Bundle extras) {
- mSyncContext = syncContext;
- mExtras = extras;
- mThreadsKey = extrasToKey(extras);
- }
-
- @Override
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
- Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, getApplication().getPackageName());
-
- SyncResult syncResult = new SyncResult();
- try {
- if (isCancelled()) return;
- // Run the sync.
- SyncService.this.onPerformSync(mExtras, syncResult);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);
- if (!isCancelled()) {
- mSyncContext.onFinished(syncResult);
- }
- // Synchronize so that the assignment will be seen by other
- // threads that also synchronize accesses to mSyncThreads.
- synchronized (mSyncThreadLock) {
- mSyncThreads.remove(mThreadsKey);
- }
- }
- }
-
- private boolean isCancelled() {
- return Thread.currentThread().isInterrupted();
- }
- }
-
- /**
- * Initiate an anonymous sync using this service. SyncAdapter-specific
- * parameters may be specified in extras, which is guaranteed to not be
- * null.
- */
- public abstract void onPerformSync(Bundle extras, SyncResult syncResult);
-
- /**
- * Override this function to indicated whether you want to support parallel syncs.
- * <p>If you override and return true multiple threads will be spawned within your Service to
- * handle each concurrent sync request.
- *
- * @return false to indicate that this service does not support parallel operations by default.
- */
- protected boolean parallelSyncsEnabled() {
- return false;
- }
-}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index c2fe3a2..791e5aa 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -84,6 +84,11 @@
*/
public static final int DOCUMENT_LAUNCH_ALWAYS = 2;
/**
+ * Constant corresponding to <code>never</code> in
+ * the {@link android.R.attr#documentLaunchMode} attribute.
+ */
+ public static final int DOCUMENT_LAUNCH_NEVER = 3;
+ /**
* 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},
@@ -99,6 +104,12 @@
public int documentLaunchMode;
/**
+ * The maximum number of tasks rooted at this activity that can be in the recent task list.
+ * Refer to {@link android.R.attr#maxRecents}.
+ */
+ public int maxRecents;
+
+ /**
* Optional name of a permission required to be able to access this
* Activity. From the "permission" attribute.
*/
diff --git a/core/java/android/content/pm/ContainerEncryptionParams.java b/core/java/android/content/pm/ContainerEncryptionParams.java
index dd1332b..ab3aa27 100644
--- a/core/java/android/content/pm/ContainerEncryptionParams.java
+++ b/core/java/android/content/pm/ContainerEncryptionParams.java
@@ -16,7 +16,7 @@
package android.content.pm;
-import android.annotation.PrivateApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -35,7 +35,7 @@
* @deprecated encrypted containers are legacy.
* @hide
*/
-@PrivateApi
+@SystemApi
@Deprecated
public class ContainerEncryptionParams implements Parcelable {
protected static final String TAG = "ContainerEncryptionParams";
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 04c0b9f..69fa408 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -67,7 +67,6 @@
*
* @param user The UserHandle of the profile that generated the change.
* @param packageName The name of the package that was removed.
- * @hide remove before ship
*/
void onPackageRemoved(UserHandle user, String packageName);
@@ -76,7 +75,6 @@
*
* @param user The UserHandle of the profile that generated the change.
* @param packageName The name of the package that was added.
- * @hide remove before ship
*/
void onPackageAdded(UserHandle user, String packageName);
@@ -85,7 +83,6 @@
*
* @param user The UserHandle of the profile that generated the change.
* @param packageName The name of the package that has changed.
- * @hide remove before ship
*/
void onPackageChanged(UserHandle user, String packageName);
@@ -99,7 +96,6 @@
* available.
* @param replacing Indicates whether these packages are replacing
* existing ones.
- * @hide remove before ship
*/
void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing);
@@ -113,59 +109,9 @@
* unavailable.
* @param replacing Indicates whether the packages are about to be
* replaced with new versions.
- * @hide remove before ship
*/
void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing);
- /**
- * Indicates that a package was removed from the specified profile.
- *
- * @param packageName The name of the package that was removed.
- * @param user The UserHandle of the profile that generated the change.
- */
- void onPackageRemoved(String packageName, UserHandle user);
-
- /**
- * Indicates that a package was added to the specified profile.
- *
- * @param packageName The name of the package that was added.
- * @param user The UserHandle of the profile that generated the change.
- */
- void onPackageAdded(String packageName, UserHandle user);
-
- /**
- * Indicates that a package was modified in the specified profile.
- *
- * @param packageName The name of the package that has changed.
- * @param user The UserHandle of the profile that generated the change.
- */
- void onPackageChanged(String packageName, UserHandle user);
-
- /**
- * Indicates that one or more packages have become available. For
- * example, this can happen when a removable storage card has
- * reappeared.
- *
- * @param packageNames The names of the packages that have become
- * available.
- * @param user The UserHandle of the profile that generated the change.
- * @param replacing Indicates whether these packages are replacing
- * existing ones.
- */
- void onPackagesAvailable(String [] packageNames, UserHandle user, boolean replacing);
-
- /**
- * Indicates that one or more packages have become unavailable. For
- * example, this can happen when a removable storage card has been
- * removed.
- *
- * @param packageNames The names of the packages that have become
- * unavailable.
- * @param user The UserHandle of the profile that generated the change.
- * @param replacing Indicates whether the packages are about to be
- * replaced with new versions.
- */
- void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing);
}
/** @hide */
@@ -361,8 +307,7 @@
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackageRemoved(user, packageName); // TODO: Remove before ship
- listener.onPackageRemoved(packageName, user);
+ listener.onPackageRemoved(user, packageName);
}
}
}
@@ -374,8 +319,7 @@
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackageChanged(user, packageName); // TODO: Remove before ship
- listener.onPackageChanged(packageName, user);
+ listener.onPackageChanged(user, packageName);
}
}
}
@@ -387,8 +331,7 @@
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackageAdded(user, packageName); // TODO: Remove before ship
- listener.onPackageAdded(packageName, user);
+ listener.onPackageAdded(user, packageName);
}
}
}
@@ -401,8 +344,7 @@
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackagesAvailable(user, packageNames, replacing); // TODO: Remove
- listener.onPackagesAvailable(packageNames, user, replacing);
+ listener.onPackagesAvailable(user, packageNames, replacing);
}
}
}
@@ -415,8 +357,7 @@
}
synchronized (LauncherApps.this) {
for (OnAppsChangedListener listener : mListeners) {
- listener.onPackagesUnavailable(user, packageNames, replacing); // TODO: Remove
- listener.onPackagesUnavailable(packageNames, user, replacing);
+ listener.onPackagesUnavailable(user, packageNames, replacing);
}
}
}
diff --git a/core/java/android/content/pm/ManifestDigest.java b/core/java/android/content/pm/ManifestDigest.java
index 943534f..1fbef7a 100644
--- a/core/java/android/content/pm/ManifestDigest.java
+++ b/core/java/android/content/pm/ManifestDigest.java
@@ -16,7 +16,7 @@
package android.content.pm;
-import android.annotation.PrivateApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Slog;
@@ -37,7 +37,7 @@
*
* @hide
*/
-@PrivateApi
+@SystemApi
public class ManifestDigest implements Parcelable {
private static final String TAG = "ManifestDigest";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c5cd5c9..550c1f1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -17,7 +17,7 @@
package android.content.pm;
import android.annotation.IntDef;
-import android.annotation.PrivateApi;
+import android.annotation.SystemApi;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.PackageInstallObserver;
@@ -370,7 +370,7 @@
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} on success.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_SUCCEEDED = 1;
/**
@@ -379,7 +379,7 @@
* already installed.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;
/**
@@ -388,7 +388,7 @@
* file is invalid.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_INVALID_APK = -2;
/**
@@ -397,7 +397,7 @@
* is invalid.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_INVALID_URI = -3;
/**
@@ -406,7 +406,7 @@
* service found that the device didn't have enough storage space to install the app.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;
/**
@@ -415,7 +415,7 @@
* package is already installed with the same name.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;
/**
@@ -424,7 +424,7 @@
* the requested shared user does not exist.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_NO_SHARED_USER = -6;
/**
@@ -434,7 +434,7 @@
* than the new package (and the old package's data was not removed).
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;
/**
@@ -444,7 +444,7 @@
* device and does not have matching signature.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;
/**
@@ -453,7 +453,7 @@
* the new package uses a shared library that is not available.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;
/**
@@ -462,7 +462,7 @@
* the new package uses a shared library that is not available.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;
/**
@@ -472,7 +472,7 @@
* either because there was not enough storage or the validation failed.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_DEXOPT = -11;
/**
@@ -482,7 +482,7 @@
* that required by the package.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_OLDER_SDK = -12;
/**
@@ -492,7 +492,7 @@
* same authority as a provider already installed in the system.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;
/**
@@ -502,7 +502,7 @@
* that required by the package.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_NEWER_SDK = -14;
/**
@@ -513,17 +513,17 @@
* flag.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_TEST_ONLY = -15;
/**
* Installation return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
* the package being installed contains native code, but none that is
- * compatible with the the device's CPU_ABI.
+ * compatible with the device's CPU_ABI.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
/**
@@ -532,7 +532,7 @@
* the new package uses a feature that is not available.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
// ------ Errors related to sdcard
@@ -542,7 +542,7 @@
* a secure container mount point couldn't be accessed on external media.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
/**
@@ -552,7 +552,7 @@
* location.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
/**
@@ -562,7 +562,7 @@
* location because the media is not available.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;
/**
@@ -571,7 +571,7 @@
* the new package couldn't be installed because the verification timed out.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_VERIFICATION_TIMEOUT = -21;
/**
@@ -580,7 +580,7 @@
* the new package couldn't be installed because the verification did not succeed.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_VERIFICATION_FAILURE = -22;
/**
@@ -589,7 +589,7 @@
* the package changed from what the calling program expected.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_PACKAGE_CHANGED = -23;
/**
@@ -615,7 +615,7 @@
* '.apk' extension.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;
/**
@@ -624,7 +624,7 @@
* if the parser was unable to retrieve the AndroidManifest.xml file.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;
/**
@@ -633,7 +633,7 @@
* if the parser encountered an unexpected exception.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;
/**
@@ -642,7 +642,7 @@
* if the parser did not find any certificates in the .apk.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;
/**
@@ -651,7 +651,7 @@
* if the parser found inconsistent certificates on the files in the .apk.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;
/**
@@ -661,7 +661,7 @@
* files in the .apk.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;
/**
@@ -670,7 +670,7 @@
* if the parser encountered a bad or missing package name in the manifest.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;
/**
@@ -679,7 +679,7 @@
* if the parser encountered a bad shared user id name in the manifest.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;
/**
@@ -688,7 +688,7 @@
* if the parser encountered some structural problem in the manifest.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;
/**
@@ -698,7 +698,7 @@
* in the manifest.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;
/**
@@ -707,7 +707,7 @@
* if the system failed to install the package because of system issues.
* @hide
*/
- @PrivateApi
+ @SystemApi
public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;
/**
@@ -1402,7 +1402,7 @@
* The device supports managed profiles for enterprise users.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_MANAGEDPROFILES = "android.software.managedprofiles";
+ public static final String FEATURE_MANAGED_PROFILES = "android.software.managed_profiles";
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
@@ -1601,7 +1601,7 @@
* <p>
* Throws {@link NameNotFoundException} if a package with the given name
* cannot be found on the system.
- *
+ *
* @param packageName The name of the package to inspect.
* @return Returns either a fully-qualified Intent that can be used to launch
* the main Leanback activity in the package, or null if the package
@@ -1615,7 +1615,7 @@
* <p>
* Throws {@link NameNotFoundException} if a package with the given name
* cannot be found on the system.
- *
+ *
* @param packageName The full name (i.e. com.google.apps.contacts) of the
* desired package.
* @return Returns an int array of the assigned gids, or null if there are
@@ -2907,7 +2907,7 @@
* instead. This method will continue to be supported but the older observer interface
* will not get additional failure details.
*/
- // @PrivateApi
+ // @SystemApi
public abstract void installPackage(
Uri packageURI, IPackageInstallObserver observer, int flags,
String installerPackageName);
@@ -2942,7 +2942,7 @@
* continue to be supported but the older observer interface will not get additional failure
* details.
*/
- // @PrivateApi
+ // @SystemApi
public abstract void installPackageWithVerification(Uri packageURI,
IPackageInstallObserver observer, int flags, String installerPackageName,
Uri verificationURI, ManifestDigest manifestDigest,
@@ -3071,7 +3071,7 @@
* on the system for other users, also install it for the calling user.
* @hide
*/
- // @PrivateApi
+ // @SystemApi
public abstract int installExistingPackage(String packageName)
throws NameNotFoundException;
@@ -3161,7 +3161,7 @@
*
* @hide
*/
- // @PrivateApi
+ // @SystemApi
public abstract void deletePackage(
String packageName, IPackageDeleteObserver observer, int flags);
@@ -3230,7 +3230,7 @@
*
* @hide
*/
- // @PrivateApi
+ // @SystemApi
public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer);
/**
@@ -3454,7 +3454,7 @@
/**
- * Return the the enabled setting for a package component (activity,
+ * Return the enabled setting for a package component (activity,
* receiver, service, provider). This returns the last value set by
* {@link #setComponentEnabledSetting(ComponentName, int, int)}; in most
* cases this value will be {@link #COMPONENT_ENABLED_STATE_DEFAULT} since
@@ -3492,14 +3492,14 @@
int newState, int flags);
/**
- * Return the the enabled setting for an application. This returns
+ * Return the enabled setting for an application. This returns
* the last value set by
* {@link #setApplicationEnabledSetting(String, int, int)}; in most
* cases this value will be {@link #COMPONENT_ENABLED_STATE_DEFAULT} since
* the value originally specified in the manifest has not been modified.
*
- * @param packageName The component to retrieve.
- * @return Returns the current enabled state for the component. May
+ * @param packageName The package name of the application to retrieve.
+ * @return Returns the current enabled state for the application. May
* be one of {@link #COMPONENT_ENABLED_STATE_ENABLED},
* {@link #COMPONENT_ENABLED_STATE_DISABLED}, or
* {@link #COMPONENT_ENABLED_STATE_DEFAULT}. The last one means the
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ab8bf61..4cac7fd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2511,6 +2511,9 @@
a.info.documentLaunchMode = sa.getInt(
com.android.internal.R.styleable.AndroidManifestActivity_documentLaunchMode,
ActivityInfo.DOCUMENT_LAUNCH_NONE);
+ a.info.maxRecents = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestActivity_maxRecents,
+ 15);
a.info.screenOrientation = sa.getInt(
com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation,
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 3737638..9625578 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -702,12 +702,17 @@
* Context.obtainStyledAttributes} with
* an array containing the resource ID of interest to create the TypedArray.</p>
*
+ * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
+ * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
+ * or {@link #getDrawable(int, Theme)} passing the desired theme.</p>
+ *
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
+ * @see #getDrawable(int, Theme)
*/
public Drawable getDrawable(int id) throws NotFoundException {
return getDrawable(id, null);
@@ -715,7 +720,9 @@
/**
* Return a drawable object associated with a particular resource ID and
- * styled for the specified theme.
+ * styled for the specified theme. Various types of objects will be
+ * returned depending on the underlying resource -- for example, a solid
+ * color, PNG image, scalable image, etc.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
@@ -755,6 +762,11 @@
* image, scalable image, etc. The Drawable API hides these implementation
* details.
*
+ * <p class="note"><strong>Note:</strong> To obtain a themed drawable, use
+ * {@link android.content.Context#getDrawable(int) Context.getDrawable(int)}
+ * or {@link #getDrawableForDensity(int, int, Theme)} passing the desired
+ * theme.</p>
+ *
* @param id The desired resource identifier, as generated by the aapt tool.
* This integer encodes the package, type, and resource entry.
* The value 0 is an invalid identifier.
@@ -2227,9 +2239,7 @@
}
// First, check whether we have a cached version of this drawable
- // that's valid for the specified theme. This may apply a theme to a
- // cached drawable that has themeable attributes but was not previously
- // themed.
+ // that was inflated against the specified theme.
if (!mPreloading) {
final Drawable cachedDrawable = getCachedDrawable(caches, key, theme);
if (cachedDrawable != null) {
@@ -2255,8 +2265,8 @@
dr = loadDrawableForCookie(value, id, theme);
}
- // If we were able to obtain a drawable, attempt to place it in the
- // appropriate cache (e.g. no theme, themed, themeable).
+ // If we were able to obtain a drawable, store it in the appropriate
+ // cache (either preload or themed).
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
cacheDrawable(value, theme, isColorDrawable, caches, key, dr);
@@ -2340,12 +2350,12 @@
if (file.endsWith(".xml")) {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXmlThemed(this, rp, theme);
+ dr = Drawable.createFromXml(this, rp, theme);
rp.close();
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- dr = Drawable.createFromResourceStreamThemed(this, value, is, file, null, theme);
+ dr = Drawable.createFromResourceStream(this, value, is, file, null);
is.close();
}
} catch (Exception e) {
@@ -2364,7 +2374,7 @@
ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
long key, Theme theme) {
synchronized (mAccessLock) {
- final int themeKey = theme != null ? theme.mThemeResId : 0;
+ final String themeKey = theme != null ? theme.mKey : "";
final LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(themeKey);
if (themedCache != null) {
final Drawable themedDrawable = getCachedDrawableLocked(themedCache, key);
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index c8de2f1..de2cc67 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -708,6 +708,7 @@
* is generated if the direction of the 2-seconds window average gravity changed by at
* least 35 degrees since the activation of the sensor. It is a wake up sensor.
*
+ * @hide
* @see #isWakeUpSensor()
*/
public static final int TYPE_WAKE_UP_TILT_DETECTOR = 41;
@@ -715,6 +716,7 @@
/**
* A constant string describing a wake up tilt detector sensor type.
*
+ * @hide
* @see #TYPE_WAKE_UP_TILT_DETECTOR
*/
public static final String SENSOR_STRING_TYPE_WAKE_UP_TILT_DETECTOR =
@@ -752,16 +754,47 @@
*/
public static final int TYPE_ALL = -1;
- /* Reporting mode constants for sensors. Each sensor will have exactly one
- reporting mode associated with it. */
- // Events are reported at a constant rate.
- static int REPORTING_MODE_CONTINUOUS = 1;
+ // If this flag is set, the sensor defined as a wake up sensor. This field and REPORTING_MODE_*
+ // constants are defined as flags in sensors.h. Modify at both places if needed.
+ private static final int SENSOR_FLAG_WAKE_UP_SENSOR = 1;
- // Events are reported only when the value changes.
- static int REPORTING_MODE_ON_CHANGE = 2;
+ /**
+ * Events are reported at a constant rate which is set by the rate parameter of
+ * {@link SensorManager#registerListener(SensorEventListener, Sensor, int)}. Note: If other
+ * applications are requesting a higher rate, the sensor data might be delivered at faster rates
+ * than requested.
+ */
+ public static final int REPORTING_MODE_CONTINUOUS = 0;
- // Upon detection of an event, the sensor deactivates itself and then sends a single event.
- static int REPORTING_MODE_ONE_SHOT = 3;
+ /**
+ * Events are reported only when the value changes. Event delivery rate can be limited by
+ * setting appropriate value for rate parameter of
+ * {@link SensorManager#registerListener(SensorEventListener, Sensor, int)} Note: If other
+ * applications are requesting a higher rate, the sensor data might be delivered at faster rates
+ * than requested.
+ */
+ public static final int REPORTING_MODE_ON_CHANGE = 1;
+
+ /**
+ * Events are reported in one-shot mode. Upon detection of an event, the sensor deactivates
+ * itself and then sends a single event. Sensors of this reporting mode must be registered to
+ * using {@link SensorManager#requestTriggerSensor(TriggerEventListener, Sensor)}.
+ */
+ public static final int REPORTING_MODE_ONE_SHOT = 2;
+
+ /**
+ * Events are reported as described in the description of the sensor. The rate passed to
+ * registerListener might not have an impact on the rate of event delivery. See the sensor
+ * definition for more information on when and how frequently the events are reported. For
+ * example, step detectors report events when a step is detected.
+ *
+ * @see SensorManager#registerListener(SensorEventListener, Sensor, int, int)
+ */
+ public static final int REPORTING_MODE_SPECIAL_TRIGGER = 3;
+
+ // Mask for the LSB 2nd, 3rd and fourth bits.
+ private static final int REPORTING_MODE_MASK = 0xE;
+ private static final int REPORTING_MODE_SHIFT = 1;
// TODO(): The following arrays are fragile and error-prone. This needs to be refactored.
@@ -770,80 +803,74 @@
// associated with
// {@link SensorEvent} or {@link TriggerEvent} for the Sensor
private static final int[] sSensorReportingModes = {
- 0, 0, // padding because sensor types start at 1
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_ACCELEROMETER
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_GEOMAGNETIC_FIELD
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_ORIENTATION
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_GYROSCOPE
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_LIGHT
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_PRESSURE
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_TEMPERATURE
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_PROXIMITY
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_GRAVITY
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_LINEAR_ACCELERATION
- REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_ROTATION_VECTOR
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_RELATIVE_HUMIDITY
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_AMBIENT_TEMPERATURE
- REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED
- REPORTING_MODE_CONTINUOUS, 4, // SENSOR_TYPE_GAME_ROTATION_VECTOR
- REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_GYROSCOPE_UNCALIBRATED
- REPORTING_MODE_ONE_SHOT, 1, // SENSOR_TYPE_SIGNIFICANT_MOTION
- // added post 4.3
- REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_DETECTOR
- REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_STEP_COUNTER
- REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR
- REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_HEART_RATE_MONITOR
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR
+ 0, // padding because sensor types start at 1
+ 3, // SENSOR_TYPE_ACCELEROMETER
+ 3, // SENSOR_TYPE_GEOMAGNETIC_FIELD
+ 3, // SENSOR_TYPE_ORIENTATION
+ 3, // SENSOR_TYPE_GYROSCOPE
+ 3, // SENSOR_TYPE_LIGHT
+ 3, // SENSOR_TYPE_PRESSURE
+ 3, // SENSOR_TYPE_TEMPERATURE
+ 3, // SENSOR_TYPE_PROXIMITY
+ 3, // SENSOR_TYPE_GRAVITY
+ 3, // SENSOR_TYPE_LINEAR_ACCELERATION
+ 5, // SENSOR_TYPE_ROTATION_VECTOR
+ 3, // SENSOR_TYPE_RELATIVE_HUMIDITY
+ 3, // SENSOR_TYPE_AMBIENT_TEMPERATURE
+ 6, // SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED
+ 4, // SENSOR_TYPE_GAME_ROTATION_VECTOR
+ 6, // SENSOR_TYPE_GYROSCOPE_UNCALIBRATED
+ 1, // SENSOR_TYPE_SIGNIFICANT_MOTION
+ 1, // SENSOR_TYPE_STEP_DETECTOR
+ 1, // SENSOR_TYPE_STEP_COUNTER
+ 5, // SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR
+ 1, // SENSOR_TYPE_HEART_RATE_MONITOR
+ 3, // SENSOR_TYPE_NON_WAKE_UP_PROXIMITY_SENSOR
// wake up variants of base sensors
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_ACCELEROMETER
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_ORIENTATION
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_GYROSCOPE
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_LIGHT
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_PRESSURE
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_GRAVITY
- REPORTING_MODE_CONTINUOUS, 3, // SENSOR_TYPE_WAKE_UP_LINEAR_ACCELERATION
- REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_WAKE_UP_ROTATION_VECTOR
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_RELATIVE_HUMIDITY
- REPORTING_MODE_ON_CHANGE, 3, // SENSOR_TYPE_WAKE_UP_AMBIENT_TEMPERATURE
- REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED
- REPORTING_MODE_CONTINUOUS, 4, // SENSOR_TYPE_WAKE_UP_GAME_ROTATION_VECTOR
- REPORTING_MODE_CONTINUOUS, 6, // SENSOR_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED
- REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_STEP_DETECTOR
- REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_STEP_COUNTER
- REPORTING_MODE_CONTINUOUS, 5, // SENSOR_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR
- REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_HEART_RATE_MONITOR
- REPORTING_MODE_ON_CHANGE, 1, // SENSOR_TYPE_WAKE_UP_TILT_DETECTOR
- REPORTING_MODE_ONE_SHOT, 1, // SENSOR_TYPE_WAKE_GESTURE
+ 3, // SENSOR_TYPE_WAKE_UP_ACCELEROMETER
+ 3, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD
+ 3, // SENSOR_TYPE_WAKE_UP_ORIENTATION
+ 3, // SENSOR_TYPE_WAKE_UP_GYROSCOPE
+ 3, // SENSOR_TYPE_WAKE_UP_LIGHT
+ 3, // SENSOR_TYPE_WAKE_UP_PRESSURE
+ 3, // SENSOR_TYPE_WAKE_UP_GRAVITY
+ 3, // SENSOR_TYPE_WAKE_UP_LINEAR_ACCELERATION
+ 5, // SENSOR_TYPE_WAKE_UP_ROTATION_VECTOR
+ 3, // SENSOR_TYPE_WAKE_UP_RELATIVE_HUMIDITY
+ 3, // SENSOR_TYPE_WAKE_UP_AMBIENT_TEMPERATURE
+ 6, // SENSOR_TYPE_WAKE_UP_MAGNETIC_FIELD_UNCALIBRATED
+ 4, // SENSOR_TYPE_WAKE_UP_GAME_ROTATION_VECTOR
+ 6, // SENSOR_TYPE_WAKE_UP_GYROSCOPE_UNCALIBRATED
+ 1, // SENSOR_TYPE_WAKE_UP_STEP_DETECTOR
+ 1, // SENSOR_TYPE_WAKE_UP_STEP_COUNTER
+ 5, // SENSOR_TYPE_WAKE_UP_GEOMAGNETIC_ROTATION_VECTOR
+ 1, // SENSOR_TYPE_WAKE_UP_HEART_RATE_MONITOR
+ 1, // SENSOR_TYPE_WAKE_UP_TILT_DETECTOR
+ 1, // SENSOR_TYPE_WAKE_GESTURE
};
- static int getReportingMode(Sensor sensor) {
- int offset = sensor.mType * 2;
- if (offset >= sSensorReportingModes.length) {
- // we don't know about this sensor, so this is probably a
- // vendor-defined sensor, in that case, we figure out the reporting
- // mode from the sensor meta-data.
- int minDelay = sensor.mMinDelay;
- if (minDelay == 0) {
- return REPORTING_MODE_ON_CHANGE;
- } else if (minDelay < 0) {
- return REPORTING_MODE_ONE_SHOT;
- } else {
- return REPORTING_MODE_CONTINUOUS;
- }
- }
- return sSensorReportingModes[offset];
+ /**
+ * Each sensor has exactly one reporting mode associated with it. This method returns the
+ * reporting mode constant for this sensor type.
+ *
+ * @return Reporting mode for the input sensor, one of REPORTING_MODE_* constants.
+ * @see #REPORTING_MODE_CONTINUOUS
+ * @see #REPORTING_MODE_ON_CHANGE
+ * @see #REPORTING_MODE_ONE_SHOT
+ * @see #REPORTING_MODE_SPECIAL_TRIGGER
+ */
+ public int getReportingMode() {
+ return ((mFlags & REPORTING_MODE_MASK) >> REPORTING_MODE_SHIFT);
}
static int getMaxLengthValuesArray(Sensor sensor, int sdkLevel) {
- int type = sensor.mType;
// RotationVector length has changed to 3 to 5 for API level 18
// Set it to 3 for backward compatibility.
- if (type == Sensor.TYPE_ROTATION_VECTOR &&
+ if (sensor.mType == Sensor.TYPE_ROTATION_VECTOR &&
sdkLevel <= Build.VERSION_CODES.JELLY_BEAN_MR1) {
return 3;
}
- int offset = type * 2 + 1;
+ int offset = sensor.mType;
if (offset >= sSensorReportingModes.length) {
// we don't know about this sensor, so this is probably a
// vendor-defined sensor, in that case, we don't know how many value
@@ -873,7 +900,7 @@
private String mStringType;
private String mRequiredPermission;
private int mMaxDelay;
- private boolean mWakeUpSensor;
+ private int mFlags;
Sensor() {
}
@@ -1016,7 +1043,7 @@
* @return true if this is a wake up sensor, false otherwise.
*/
public boolean isWakeUpSensor() {
- return mWakeUpSensor;
+ return (mFlags & SENSOR_FLAG_WAKE_UP_SENSOR) != 0;
}
void setRange(float max, float res) {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index b66ec86..a6c3ea4 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -99,7 +99,7 @@
return false;
}
// Trigger Sensors should use the requestTriggerSensor call.
- if (Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) {
+ if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
Log.e(TAG, "Trigger Sensors should use the requestTriggerSensor.");
return false;
}
@@ -133,7 +133,7 @@
@Override
protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
// Trigger Sensors should use the cancelTriggerSensor call.
- if (sensor != null && Sensor.getReportingMode(sensor) == Sensor.REPORTING_MODE_ONE_SHOT) {
+ if (sensor != null && sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
return;
}
@@ -159,7 +159,7 @@
protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
- if (Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) return false;
+ if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) return false;
synchronized (mTriggerListeners) {
TriggerEventQueue queue = mTriggerListeners.get(listener);
@@ -181,7 +181,7 @@
@Override
protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor,
boolean disable) {
- if (sensor != null && Sensor.getReportingMode(sensor) != Sensor.REPORTING_MODE_ONE_SHOT) {
+ if (sensor != null && sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
return false;
}
synchronized (mTriggerListeners) {
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 7738d2d..d98bdc2 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -29,19 +29,19 @@
*
* <p>Creating a session is an expensive operation and can take several hundred milliseconds, since
* it requires configuring the camera device's internal pipelines and allocating memory buffers for
- * sending images to the desired targets. While
- * {@link CameraDevice#createCaptureSession createCaptureSession} will provide a
- * CameraCaptureSession object immediately, configuration won't be complete until the
- * {@link CameraCaptureSession.StateListener#onConfigured onConfigured} callback is called for the
- * first time. If configuration cannot be completed, then the
+ * sending images to the desired targets. Therefore the setup is done asynchronously, and
+ * {@link CameraDevice#createCaptureSession createCaptureSession} will send the ready-to-use
+ * CameraCaptureSession to the provided listener's
+ * {@link CameraCaptureSession.StateListener#onConfigured onConfigured} callback. If configuration
+ * cannot be completed, then the
* {@link CameraCaptureSession.StateListener#onConfigureFailed onConfigureFailed} is called, and the
* session will not become active.</p>
- *
+ *<!--
* <p>Any capture requests (repeating or non-repeating) submitted before the session is ready will
* be queued up and will begin capture once the session becomes ready. In case the session cannot be
* configured and {@link StateListener#onConfigureFailed onConfigureFailed} is called, all queued
* capture requests are discarded.</p>
- *
+ *-->
* <p>If a new session is created by the camera device, then the previous session is closed, and its
* associated {@link StateListener#onClosed onClosed} callback will be invoked. All
* of the session methods will throw an IllegalStateException if called once the session is
@@ -54,7 +54,7 @@
public abstract class CameraCaptureSession implements AutoCloseable {
/**
- * Get the camera device that this session is created for
+ * Get the camera device that this session is created for.
*/
public abstract CameraDevice getDevice();
@@ -90,8 +90,9 @@
*
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
- * @throws IllegalStateException if this session is no longer active, either because a new
- * session has been created or the camera device has been closed.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
* @throws IllegalArgumentException if the request targets Surfaces that are not configured as
* outputs for this session. Or if the handler is null, the
* listener is not null, and the calling thread has no looper.
@@ -99,6 +100,7 @@
* @see #captureBurst
* @see #setRepeatingRequest
* @see #setRepeatingBurst
+ * @see #abortCaptures
*/
public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler)
throws CameraAccessException;
@@ -132,8 +134,9 @@
*
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
- * @throws IllegalStateException if this session is no longer active, either because a new
- * session has been created or the camera device has been closed.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
* @throws IllegalArgumentException If the requests target Surfaces not currently configured as
* outputs. Or if the handler is null, the listener is not
* null, and the calling thread has no looper.
@@ -141,6 +144,7 @@
* @see #capture
* @see #setRepeatingRequest
* @see #setRepeatingBurst
+ * @see #abortCaptures
*/
public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException;
@@ -162,10 +166,6 @@
* capture request will be processed before any further repeating
* requests are processed.<p>
*
- * <p>Repeating requests are a simple way for an application to maintain a
- * preview or other continuous stream of frames, without having to submit
- * requests through {@link #capture} at video rates.</p>
- *
* <p>To stop the repeating capture, call {@link #stopRepeating}. Calling
* {@link #abortCaptures} will also clear the request.</p>
*
@@ -188,11 +188,13 @@
*
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
- * @throws IllegalStateException if this session is no longer active, either because a new
- * session has been created or the camera device has been closed.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
* @throws IllegalArgumentException If the requests reference Surfaces that are not currently
* configured as outputs. Or if the handler is null, the
* listener is not null, and the calling thread has no looper.
+ * Or if no requests were passed in.
*
* @see #capture
* @see #captureBurst
@@ -246,11 +248,13 @@
*
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
- * @throws IllegalStateException if this session is no longer active, either because a new
- * session has been created or the camera device has been closed.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
* @throws IllegalArgumentException If the requests reference Surfaces not currently configured
* as outputs. Or if the handler is null, the listener is not
- * null, and the calling thread has no looper.
+ * null, and the calling thread has no looper. Or if no
+ * requests were passed in.
*
* @see #capture
* @see #captureBurst
@@ -274,8 +278,9 @@
*
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
- * @throws IllegalStateException if this session is no longer active, either because a new
- * session has been created or the camera device has been closed.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
*
* @see #setRepeatingRequest
* @see #setRepeatingBurst
@@ -308,20 +313,21 @@
*
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
- * @throws IllegalStateException if this session is no longer active, either because a new
- * session has been created or the camera device has been closed.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
*
* @see #setRepeatingRequest
* @see #setRepeatingBurst
- * @see #configureOutputs
+ * @see CameraDevice#createCaptureSession
*/
public abstract void abortCaptures() throws CameraAccessException;
/**
* Close this capture session asynchronously.
*
- * <p>Closing a session frees up the target output Surfaces of the session for reuse with either a
- * new session, or to other APIs that can draw to Surfaces.</p>
+ * <p>Closing a session frees up the target output Surfaces of the session for reuse with either
+ * a new session, or to other APIs that can draw to Surfaces.</p>
*
* <p>Note that creating a new capture session with {@link CameraDevice#createCaptureSession}
* will close any existing capture session automatically, and call the older session listener's
@@ -334,6 +340,8 @@
* However, any in-progress capture requests submitted to the session will be completed as
* normal; once all captures have completed and the session has been torn down,
* {@link StateListener#onClosed} will be called.</p>
+ *
+ * <p>Closing a session is idempotent; closing more than once has no effect.</p>
*/
@Override
public abstract void close();
@@ -358,6 +366,7 @@
* <p>If the camera device configuration fails, then {@link #onConfigureFailed} will
* be invoked instead of this callback.</p>
*
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
public abstract void onConfigured(CameraCaptureSession session);
@@ -371,6 +380,8 @@
* callback is invoked will throw an IllegalStateException. Any capture requests submitted
* to the session prior to this callback will be discarded and will not produce any
* callbacks on their listeners.</p>
+ *
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
public abstract void onConfigureFailed(CameraCaptureSession session);
@@ -384,6 +395,8 @@
* <p>Otherwise, this callback will be invoked any time the session finishes processing
* all of its active capture requests, and no repeating request or burst is set up.</p>
*
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
+ *
*/
public void onReady(CameraCaptureSession session) {
// default empty implementation
@@ -398,6 +411,8 @@
*
* <p>If the session runs out of capture requests to process and calls {@link #onReady},
* then this callback will be invoked again once new requests are submitted for capture.</p>
+ *
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
public void onActive(CameraCaptureSession session) {
// default empty implementation
@@ -414,6 +429,8 @@
* any repeating requests or bursts are stopped (as if {@link #stopRepeating()} was called).
* However, any in-progress capture requests submitted to the session will be completed
* as normal.</p>
+ *
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
*/
public void onClosed(CameraCaptureSession session) {
// default empty implementation
@@ -466,13 +483,13 @@
*
* <p>The default implementation of this method does nothing.</p>
*
- * @param camera the CameraDevice sending the callback
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
* @param request the request for the capture that just begun
* @param timestamp the timestamp at start of capture, in nanoseconds.
*
* @see android.media.MediaActionSound
*/
- public void onCaptureStarted(CameraDevice camera,
+ public void onCaptureStarted(CameraCaptureSession session,
CaptureRequest request, long timestamp) {
// default empty implementation
}
@@ -490,7 +507,7 @@
*
* <p>The default implementation of this method does nothing.</p>
*
- * @param camera The CameraDevice sending the callback.
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
* @param request The request that was given to the CameraDevice
* @param result The partial output metadata from the capture, which
* includes a subset of the CaptureResult fields.
@@ -502,7 +519,7 @@
*
* @hide
*/
- public void onCapturePartial(CameraDevice camera,
+ public void onCapturePartial(CameraCaptureSession session,
CaptureRequest request, CaptureResult result) {
// default empty implementation
}
@@ -533,7 +550,7 @@
*
* <p>The default implementation of this method does nothing.</p>
*
- * @param camera The CameraDevice sending the callback.
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
* @param request The request that was given to the CameraDevice
* @param partialResult The partial output metadata from the capture, which
* includes a subset of the {@link TotalCaptureResult} fields.
@@ -543,7 +560,7 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureProgressed(CameraDevice camera,
+ public void onCaptureProgressed(CameraCaptureSession session,
CaptureRequest request, CaptureResult partialResult) {
// default empty implementation
}
@@ -561,7 +578,7 @@
*
* <p>The default implementation of this method does nothing.</p>
*
- * @param camera The CameraDevice sending the callback.
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
* @param request The request that was given to the CameraDevice
* @param result The total output metadata from the capture, including the
* final capture parameters and the state of the camera system during
@@ -572,7 +589,7 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureCompleted(CameraDevice camera,
+ public void onCaptureCompleted(CameraCaptureSession session,
CaptureRequest request, TotalCaptureResult result) {
// default empty implementation
}
@@ -588,8 +605,8 @@
*
* <p>The default implementation of this method does nothing.</p>
*
- * @param camera
- * The CameraDevice sending the callback.
+ * @param session
+ * The session returned by {@link CameraDevice#createCaptureSession}
* @param request
* The request that was given to the CameraDevice
* @param failure
@@ -601,7 +618,7 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
*/
- public void onCaptureFailed(CameraDevice camera,
+ public void onCaptureFailed(CameraCaptureSession session,
CaptureRequest request, CaptureFailure failure) {
// default empty implementation
}
@@ -617,8 +634,8 @@
*
* <p>The default implementation does nothing.</p>
*
- * @param camera
- * The CameraDevice sending the callback.
+ * @param session
+ * The session returned by {@link CameraDevice#createCaptureSession}
* @param sequenceId
* A sequence ID returned by the {@link #capture} family of functions.
* @param frameNumber
@@ -631,7 +648,7 @@
* @see CaptureFailure#getSequenceId()
* @see #onCaptureSequenceAborted
*/
- public void onCaptureSequenceCompleted(CameraDevice camera,
+ public void onCaptureSequenceCompleted(CameraCaptureSession session,
int sequenceId, long frameNumber) {
// default empty implementation
}
@@ -649,8 +666,8 @@
*
* <p>The default implementation does nothing.</p>
*
- * @param camera
- * The CameraDevice sending the callback.
+ * @param session
+ * The session returned by {@link CameraDevice#createCaptureSession}
* @param sequenceId
* A sequence ID returned by the {@link #capture} family of functions.
*
@@ -660,7 +677,7 @@
* @see CaptureFailure#getSequenceId()
* @see #onCaptureSequenceCompleted
*/
- public void onCaptureSequenceAborted(CameraDevice camera,
+ public void onCaptureSequenceAborted(CameraCaptureSession session,
int sequenceId) {
// default empty implementation
}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 2f5b4fe..9eea545 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -30,7 +30,7 @@
*
* <p>These properties are fixed for a given CameraDevice, and can be queried
* through the {@link CameraManager CameraManager}
- * interface in addition to through the CameraDevice interface.</p>
+ * interface with {@link CameraManager#getCameraCharacteristics}.</p>
*
* <p>{@link CameraCharacteristics} objects are immutable.</p>
*
@@ -320,7 +320,7 @@
/**
* <p>List of frame rate ranges supported by the
- * AE algorithm/hardware</p>
+ * auto-exposure (AE) algorithm/hardware</p>
*/
public static final Key<android.util.Range<Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES =
new Key<android.util.Range<Integer>[]>("android.control.aeAvailableTargetFpsRanges", new TypeReference<android.util.Range<Integer>[]>() {{ }});
@@ -343,7 +343,7 @@
new Key<Rational>("android.control.aeCompensationStep", Rational.class);
/**
- * <p>List of AF modes that can be
+ * <p>List of auto-focus (AF) modes that can be
* selected with {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}.</p>
* <p>Not all the auto-focus modes may be supported by a
* given camera device. This entry lists the valid modes for
@@ -496,7 +496,7 @@
new Key<int[]>("android.hotPixel.availableHotPixelModes", int[].class);
/**
- * <p>Supported resolutions for the JPEG thumbnail</p>
+ * <p>Supported resolutions for the JPEG thumbnail.</p>
* <p>Below condiditions will be satisfied for this size list:</p>
* <ul>
* <li>The sizes will be sorted by increasing pixel area (width x height).
@@ -555,7 +555,7 @@
* <p>List containing a subset of the optical image
* stabilization (OIS) modes specified in
* {@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}.</p>
- * <p>If OIS is not implemented for a given camera device, this should
+ * <p>If OIS is not implemented for a given camera device, this will
* contain only OFF.</p>
*
* @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
@@ -612,7 +612,7 @@
/**
* <p>Direction the camera faces relative to
- * device screen</p>
+ * device screen.</p>
* @see #LENS_FACING_FRONT
* @see #LENS_FACING_BACK
*/
@@ -622,7 +622,7 @@
/**
* <p>The set of noise reduction modes supported by this camera device.</p>
* <p>This tag lists the valid modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}.</p>
- * <p>Full-capability camera devices must laways support OFF and FAST.</p>
+ * <p>Full-capability camera devices must always support OFF and FAST.</p>
*
* @see CaptureRequest#NOISE_REDUCTION_MODE
*/
@@ -751,6 +751,7 @@
* <p>For example, for Zero Shutter Lag (ZSL) still capture use case, the input
* stream image format will be RAW_OPAQUE, the associated output stream image format
* should be JPEG.</p>
+ * @hide
*/
public static final Key<Integer> REQUEST_MAX_NUM_INPUT_STREAMS =
new Key<Integer>("android.request.maxNumInputStreams", int.class);
@@ -777,17 +778,20 @@
new Key<Byte>("android.request.pipelineMaxDepth", byte.class);
/**
- * <p>Optional. Defaults to 1. Defines how many sub-components
+ * <p>Defines how many sub-components
* a result will be composed of.</p>
* <p>In order to combat the pipeline latency, partial results
* may be delivered to the application layer from the camera device as
* soon as they are available.</p>
- * <p>A value of 1 means that partial results are not supported.</p>
- * <p>A typical use case for this might be: after requesting an AF lock the
- * new AF state might be available 50% of the way through the pipeline.
- * The camera device could then immediately dispatch this state via a
- * partial result to the framework/application layer, and the rest of
- * the metadata via later partial results.</p>
+ * <p>Optional; defaults to 1. A value of 1 means that partial
+ * results are not supported, and only the final TotalCaptureResult will
+ * be produced by the camera device.</p>
+ * <p>A typical use case for this might be: after requesting an
+ * auto-focus (AF) lock the new AF state might be available 50%
+ * of the way through the pipeline. The camera device could
+ * then immediately dispatch this state via a partial result to
+ * the application, and the rest of the metadata via later
+ * partial results.</p>
*/
public static final Key<Integer> REQUEST_PARTIAL_RESULT_COUNT =
new Key<Integer>("android.request.partialResultCount", int.class);
@@ -804,22 +808,18 @@
* to do this query each of android.request.availableRequestKeys,
* android.request.availableResultKeys,
* android.request.availableCharacteristicsKeys.</p>
- * <p>XX: Maybe these should go into {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel}
- * as a table instead?</p>
* <p>The following capabilities are guaranteed to be available on
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> FULL devices:</p>
* <ul>
* <li>MANUAL_SENSOR</li>
+ * <li>MANUAL_POST_PROCESSING</li>
* </ul>
* <p>Other capabilities may be available on either FULL or LIMITED
- * devices, but the app. should query this field to be sure.</p>
+ * devices, but the application should query this field to be sure.</p>
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
- * @see #REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
- * @see #REQUEST_AVAILABLE_CAPABILITIES_OPTIONAL
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
- * @see #REQUEST_AVAILABLE_CAPABILITIES_ZSL
* @see #REQUEST_AVAILABLE_CAPABILITIES_DNG
*/
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -835,7 +835,6 @@
* at a more granular level than capabilities. This is especially
* important for optional keys that are not listed under any capability
* in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
- * <p>TODO: This should be used by #getAvailableCaptureRequestKeys.</p>
*
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
* @hide
@@ -860,7 +859,6 @@
* at a more granular level than capabilities. This is especially
* important for optional keys that are not listed under any capability
* in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p>
- * <p>TODO: This should be used by #getAvailableCaptureResultKeys.</p>
*
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
* @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
@@ -876,7 +874,6 @@
* android.request.availableResultKeys (except that it applies for
* CameraCharacteristics instead of CaptureResult). See above for more
* details.</p>
- * <p>TODO: This should be used by CameraCharacteristics#getKeys.</p>
* @hide
*/
public static final Key<int[]> REQUEST_AVAILABLE_CHARACTERISTICS_KEYS =
@@ -924,10 +921,15 @@
new Key<android.util.Size[]>("android.scaler.availableJpegSizes", android.util.Size[].class);
/**
- * <p>The maximum ratio between active area width
- * and crop region width, or between active area height and
- * crop region height, if the crop region height is larger
- * than width</p>
+ * <p>The maximum ratio between both active area width
+ * and crop region width, and active area height and
+ * crop region height.</p>
+ * <p>This represents the maximum amount of zooming possible by
+ * the camera device, or equivalently, the minimum cropping
+ * window size.</p>
+ * <p>Crop regions that have a width or height that is smaller
+ * than this ratio allows will be rounded up to the minimum
+ * allowed size by the camera device.</p>
*/
public static final Key<Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM =
new Key<Float>("android.scaler.availableMaxDigitalZoom", float.class);
@@ -973,7 +975,7 @@
* <p>The mapping of image formats that are supported by this
* camera device for input streams, to their corresponding output formats.</p>
* <p>All camera devices with at least 1
- * {@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} will have at least one
+ * android.request.maxNumInputStreams will have at least one
* available input format.</p>
* <p>The camera device will support the following map of formats,
* if its dependent capability is supported:</p>
@@ -1020,8 +1022,6 @@
* <p>Attempting to configure an input stream with output streams not
* listed as available in this map is not valid.</p>
* <p>TODO: typedef to ReprocessFormatMap</p>
- *
- * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
* @hide
*/
public static final Key<int[]> SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP =
@@ -1033,8 +1033,6 @@
* (i.e. format, width, height, output/input stream).</p>
* <p>The configurations are listed as <code>(format, width, height, input?)</code>
* tuples.</p>
- * <p>All camera devices will support sensor maximum resolution (defined by
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}) for the JPEG format.</p>
* <p>For a given use case, the actual maximum supported resolution
* may be lower than what is listed here, depending on the destination
* Surface for the image data. For example, for recording video,
@@ -1328,15 +1326,21 @@
new Key<android.graphics.Rect>("android.sensor.info.activeArraySize", android.graphics.Rect.class);
/**
- * <p>Range of valid sensitivities</p>
+ * <p>Range of valid sensitivities.</p>
+ * <p>The minimum and maximum valid values for the
+ * {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} control.</p>
+ * <p>The values are the standard ISO sensitivity values,
+ * as defined in ISO 12232:2006.</p>
+ *
+ * @see CaptureRequest#SENSOR_SENSITIVITY
*/
public static final Key<android.util.Range<Integer>> SENSOR_INFO_SENSITIVITY_RANGE =
new Key<android.util.Range<Integer>>("android.sensor.info.sensitivityRange", new TypeReference<android.util.Range<Integer>>() {{ }});
/**
- * <p>Arrangement of color filters on sensor;
+ * <p>The arrangement of color filters on sensor;
* represents the colors in the top-left 2x2 section of
- * the sensor, in reading order</p>
+ * the sensor, in reading order.</p>
* @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB
* @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG
* @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG
@@ -1374,8 +1378,11 @@
/**
* <p>The physical dimensions of the full pixel
- * array</p>
- * <p>Needed for FOV calculation for old API</p>
+ * array.</p>
+ * <p>This is the physical size of the sensor pixel
+ * array defined by {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
*/
public static final Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE =
new Key<android.util.SizeF>("android.sensor.info.physicalSize", android.util.SizeF.class);
@@ -1383,9 +1390,17 @@
/**
* <p>Dimensions of full pixel array, possibly
* including black calibration pixels.</p>
- * <p>Maximum output resolution for raw format must
- * match this in
- * android.scaler.availableStreamConfigurations.</p>
+ * <p>The pixel count of the full pixel array,
+ * which covers {@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize} area.</p>
+ * <p>If a camera device supports raw sensor formats, either this
+ * or {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} is the maximum output
+ * raw size listed in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.
+ * If a size corresponding to pixelArraySize is listed, the resulting
+ * raw sensor image will include black pixels.</p>
+ *
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
*/
public static final Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE =
new Key<android.util.Size>("android.sensor.info.pixelArraySize", android.util.Size.class);
@@ -1640,8 +1655,8 @@
new Key<Integer>("android.sensor.orientation", int.class);
/**
- * <p>Optional. Defaults to [OFF]. Lists the supported test
- * pattern modes for {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode}.</p>
+ * <p>Lists the supported sensor test pattern modes for {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode}.</p>
+ * <p>Optional. Defaults to [OFF].</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CaptureRequest#SENSOR_TEST_PATTERN_MODE
@@ -1650,10 +1665,9 @@
new Key<int[]>("android.sensor.availableTestPatternModes", int[].class);
/**
- * <p>Which face detection modes are available,
- * if any</p>
- * <p>OFF means face detection is disabled, it must
- * be included in the list.</p>
+ * <p>The face detection modes that are available
+ * for this camera device.</p>
+ * <p>OFF is always supported.</p>
* <p>SIMPLE means the device supports the
* android.statistics.faceRectangles and
* android.statistics.faceScores outputs.</p>
@@ -1665,8 +1679,8 @@
new Key<int[]>("android.statistics.info.availableFaceDetectModes", int[].class);
/**
- * <p>Maximum number of simultaneously detectable
- * faces</p>
+ * <p>The maximum number of simultaneously detectable
+ * faces.</p>
*/
public static final Key<Integer> STATISTICS_INFO_MAX_FACE_COUNT =
new Key<Integer>("android.statistics.info.maxFaceCount", int.class);
@@ -1675,7 +1689,7 @@
* <p>The set of hot pixel map output modes supported by this camera device.</p>
* <p>This tag lists valid output modes for {@link CaptureRequest#STATISTICS_HOT_PIXEL_MAP_MODE android.statistics.hotPixelMapMode}.</p>
* <p>If no hotpixel map is available for this camera device, this will contain
- * only OFF. If the hotpixel map is available, this should include both
+ * only OFF. If the hotpixel map is available, this will include both
* the ON and OFF options.</p>
*
* @see CaptureRequest#STATISTICS_HOT_PIXEL_MAP_MODE
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 6f5099b..1f89d33 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -24,7 +24,7 @@
import java.util.List;
/**
- * <p>The CameraDevice class is an interface to a single camera connected to an
+ * <p>The CameraDevice class is a representation of a single camera connected to an
* Android device, allowing for fine-grain control of image capture and
* post-processing at high frame rates.</p>
*
@@ -46,13 +46,13 @@
* @see CameraManager#openCamera
* @see android.Manifest.permission#CAMERA
*/
-public interface CameraDevice extends AutoCloseable {
+public abstract class CameraDevice implements AutoCloseable {
/**
* Create a request suitable for a camera preview window. Specifically, this
* means that high frame rate is given priority over the highest-quality
* post-processing. These requests would normally be used with the
- * {@link #setRepeatingRequest} method.
+ * {@link CameraCaptureSession#setRepeatingRequest} method.
*
* @see #createCaptureRequest
*/
@@ -61,7 +61,7 @@
/**
* Create a request suitable for still image capture. Specifically, this
* means prioritizing image quality over frame rate. These requests would
- * commonly be used with the {@link #capture} method.
+ * commonly be used with the {@link CameraCaptureSession#capture} method.
*
* @see #createCaptureRequest
*/
@@ -71,7 +71,7 @@
* Create a request suitable for video recording. Specifically, this means
* that a stable frame rate is used, and post-processing is set for
* recording quality. These requests would commonly be used with the
- * {@link #setRepeatingRequest} method.
+ * {@link CameraCaptureSession#setRepeatingRequest} method.
*
* @see #createCaptureRequest
*/
@@ -81,8 +81,8 @@
* Create a request suitable for still image capture while recording
* video. Specifically, this means maximizing image quality without
* disrupting the ongoing recording. These requests would commonly be used
- * with the {@link #capture} method while a request based on
- * {@link #TEMPLATE_RECORD} is is in use with {@link #setRepeatingRequest}.
+ * with the {@link CameraCaptureSession#capture} method while a request based on
+ * {@link #TEMPLATE_RECORD} is is in use with {@link CameraCaptureSession#setRepeatingRequest}.
*
* @see #createCaptureRequest
*/
@@ -127,124 +127,15 @@
* @see CameraManager#getCameraCharacteristics
* @see CameraManager#getCameraIdList
*/
- public String getId();
+ public abstract String getId();
/**
* <p>Set up a new output set of Surfaces for the camera device.</p>
*
- * <p>The configuration determines the set of potential output Surfaces for
- * the camera device for each capture request. A given request may use all
- * or a only some of the outputs. This method must be called before requests
- * can be submitted to the camera with {@link #capture capture},
- * {@link #captureBurst captureBurst},
- * {@link #setRepeatingRequest setRepeatingRequest}, or
- * {@link #setRepeatingBurst setRepeatingBurst}.</p>
- *
- * <p>Surfaces suitable for inclusion as a camera output can be created for
- * various use cases and targets:</p>
- *
- * <ul>
- *
- * <li>For drawing to a {@link android.view.SurfaceView SurfaceView}: Set
- * the size of the Surface with
- * {@link android.view.SurfaceHolder#setFixedSize} to be one of the
- * supported
- * {@link StreamConfigurationMap#getOutputSizes(Class) processed sizes}
- * before calling {@link android.view.SurfaceHolder#getSurface}.</li>
- *
- * <li>For accessing through an OpenGL texture via a
- * {@link android.graphics.SurfaceTexture SurfaceTexture}: Set the size of
- * the SurfaceTexture with
- * {@link android.graphics.SurfaceTexture#setDefaultBufferSize} to be one
- * of the supported
- * {@link StreamConfigurationMap#getOutputSizes(Class) processed sizes}
- * before creating a Surface from the SurfaceTexture with
- * {@link Surface#Surface}.</li>
- *
- * <li>For recording with {@link android.media.MediaCodec}: Call
- * {@link android.media.MediaCodec#createInputSurface} after configuring
- * the media codec to use one of the
- * {@link StreamConfigurationMap#getOutputSizes(Class) processed sizes}
- * </li>
- *
- * <li>For recording with {@link android.media.MediaRecorder}: TODO</li>
- *
- * <li>For efficient YUV processing with {@link android.renderscript}:
- * Create a RenderScript
- * {@link android.renderscript.Allocation Allocation} with a supported YUV
- * type, the IO_INPUT flag, and one of the supported
- * {@link StreamConfigurationMap#getOutputSizes(int) processed sizes}. Then
- * obtain the Surface with
- * {@link android.renderscript.Allocation#getSurface}.</li>
- *
- * <li>For access to uncompressed or {@link ImageFormat#JPEG JPEG} data in the application:
- * Create a {@link android.media.ImageReader} object with the desired
- * {@link StreamConfigurationMap#getOutputFormats() image format}, and a size from the matching
- * {@link StreamConfigurationMap#getOutputSizes(int) processed size} and {@code format}.
- * Then obtain a {@link Surface} from it.</li>
- * </ul>
- *
- * </p>
- *
- * <p>This function can take several hundred milliseconds to execute, since
- * camera hardware may need to be powered on or reconfigured.</p>
- *
- * <p>The camera device will query each Surface's size and formats upon this
- * call, so they must be set to a valid setting at this time (in particular:
- * if the format is user-visible, it must be one of
- * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of
- * {@link StreamConfigurationMap#getOutputSizes(int)}).</p>
- *
- * <p>When this method is called with valid Surfaces, the device will transition to the {@link
- * StateListener#onBusy busy state}. Once configuration is complete, the device will transition
- * into the {@link StateListener#onIdle idle state}. Capture requests using the newly-configured
- * Surfaces may then be submitted with {@link #capture}, {@link #captureBurst}, {@link
- * #setRepeatingRequest}, or {@link #setRepeatingBurst}.</p>
- *
- * <p>If this method is called while the camera device is still actively processing previously
- * submitted captures, then the following sequence of events occurs: The device transitions to
- * the busy state and calls the {@link StateListener#onBusy} callback. Second, if a repeating
- * request is set it is cleared. Third, the device finishes up all in-flight and pending
- * requests. Finally, once the device is idle, it then reconfigures its outputs, and calls the
- * {@link StateListener#onIdle} method once it is again ready to accept capture
- * requests. Therefore, no submitted work is discarded. To idle as fast as possible, use {@link
- * #flush} and wait for the idle callback before calling configureOutputs. This will discard
- * work, but reaches the new configuration sooner.</p>
- *
- * <p>Using larger resolution outputs, or more outputs, can result in slower
- * output rate from the device.</p>
- *
- * <p>Configuring the outputs with an empty or null list will transition the camera into an
- * {@link StateListener#onUnconfigured unconfigured state} instead of the {@link
- * StateListener#onIdle idle state}. </p>
- *
- * <p>Calling configureOutputs with the same arguments as the last call to
- * configureOutputs has no effect, and the {@link StateListener#onBusy busy}
- * and {@link StateListener#onIdle idle} state transitions will happen
- * immediately.</p>
- *
- * @param outputs The new set of Surfaces that should be made available as
- * targets for captured image data.
- *
- * @throws IllegalArgumentException if the set of output Surfaces do not
- * meet the requirements
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera device is not idle, or
- * if the camera device has been closed
- *
- * @see StateListener#onBusy
- * @see StateListener#onIdle
- * @see StateListener#onActive
- * @see StateListener#onUnconfigured
- * @see #stopRepeating
- * @see #flush
- * @see StreamConfigurationMap#getOutputFormats()
- * @see StreamConfigurationMap#getOutputSizes(int)
- * @see StreamConfigurationMap#getOutputSizes(Class)
* @deprecated Use {@link #createCaptureSession} instead
*/
- public void configureOutputs(List<Surface> outputs) throws CameraAccessException;
+ @Deprecated
+ public abstract void configureOutputs(List<Surface> outputs) throws CameraAccessException;
/**
* <p>Create a new camera capture session by providing the target output set of Surfaces to the
@@ -263,10 +154,11 @@
*
* <ul>
*
- * <li>For drawing to a {@link android.view.SurfaceView SurfaceView}: Set the size of the
- * Surface with {@link android.view.SurfaceHolder#setFixedSize} to be one of the sizes
- * returned by
- * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(SurfaceView.class)}
+ * <li>For drawing to a {@link android.view.SurfaceView SurfaceView}: Once the SurfaceView's
+ * Surface is {@link android.view.SurfaceHolder.Callback#surfaceCreated created}, set the
+ * size of the Surface with {@link android.view.SurfaceHolder#setFixedSize} to be one of the
+ * sizes returned by
+ * {@link StreamConfigurationMap#getOutputSizes(Class) getOutputSizes(SurfaceHolder.class)}
* and then obtain the Surface by calling {@link android.view.SurfaceHolder#getSurface}.</li>
*
* <li>For accessing through an OpenGL texture via a
@@ -299,22 +191,18 @@
* Then obtain the Surface with
* {@link android.renderscript.Allocation#getSurface}.</li>
*
- * <li>For access to raw, uncompressed or JPEG data in the application: Create a
- * {@link android.media.ImageReader} object with the one of the supported
- * {@link StreamConfigurationMap#getOutputFormats() output image formats}, and a
- * size from the supported
- * {@link StreamConfigurationMap#getOutputSizes(int) sizes for that format}. Then obtain
- * a Surface from it with {@link android.media.ImageReader#getSurface}.</li>
+ * <li>For access to raw, uncompressed JPEG data in the application: Create an
+ * {@link android.media.ImageReader} object with one of the supported output formats given by
+ * {@link StreamConfigurationMap#getOutputFormats()}, setting its size to one of the
+ * corresponding supported sizes by passing the chosen output format into
+ * {@link StreamConfigurationMap#getOutputSizes(int)}. Then obtain a
+ * {@link android.view.Surface} from it with {@link android.media.ImageReader#getSurface()}.
+ * </li>
*
* </ul>
*
- * </p>
- *
* <p>The camera device will query each Surface's size and formats upon this
- * call, so they must be set to a valid setting at this time (in particular:
- * if the format is user-visible, it must be one of
- * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of
- * {@link StreamConfigurationMap#getOutputSizes(int)}).</p>
+ * call, so they must be set to a valid setting at this time.</p>
*
* <p>It can take several hundred milliseconds for the session's configuration to complete,
* since camera hardware may need to be powered on or reconfigured. Once the configuration is
@@ -341,10 +229,7 @@
* @param listener The listener to notify about the status of the new capture session.
* @param handler The handler on which the listener should be invoked, or {@code null} to use
* the current thread's {@link android.os.Looper looper}.
- * <!--
- * @return A new camera capture session to use, or null if an empty/null set of Surfaces is
- * provided.
- * -->
+ *
* @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
* the listener is null, or the handler is null but the current
* thread has no looper.
@@ -357,7 +242,7 @@
* @see StreamConfigurationMap#getOutputSizes(int)
* @see StreamConfigurationMap#getOutputSizes(Class)
*/
- public void createCaptureSession(List<Surface> outputs,
+ public abstract void createCaptureSession(List<Surface> outputs,
CameraCaptureSession.StateListener listener, Handler handler)
throws CameraAccessException;
@@ -386,278 +271,64 @@
* @see #TEMPLATE_VIDEO_SNAPSHOT
* @see #TEMPLATE_MANUAL
*/
- public CaptureRequest.Builder createCaptureRequest(int templateType)
+ public abstract CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException;
/**
* <p>Submit a request for an image to be captured by this CameraDevice.</p>
*
- * <p>The request defines all the parameters for capturing the single image,
- * including sensor, lens, flash, and post-processing settings.</p>
- *
- * <p>Each request will produce one {@link CaptureResult} and produce new
- * frames for one or more target Surfaces, set with the CaptureRequest
- * builder's {@link CaptureRequest.Builder#addTarget} method. The target
- * surfaces must be configured as active outputs with
- * {@link #configureOutputs} before calling this method.</p>
- *
- * <p>Multiple requests can be in progress at once. They are processed in
- * first-in, first-out order, with minimal delays between each
- * capture. Requests submitted through this method have higher priority than
- * those submitted through {@link #setRepeatingRequest} or
- * {@link #setRepeatingBurst}, and will be processed as soon as the current
- * repeat/repeatBurst processing completes.</p>
- *
- * @param request the settings for this capture
- * @param listener The callback object to notify once this request has been
- * processed. If null, no metadata will be produced for this capture,
- * although image data will still be produced.
- * @param handler the handler on which the listener should be invoked, or
- * {@code null} to use the current thread's {@link android.os.Looper
- * looper}.
- *
- * @return int A unique capture sequence ID used by
- * {@link CaptureListener#onCaptureSequenceCompleted}.
- *
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera is currently busy or unconfigured,
- * or the camera device has been closed.
- * @throws IllegalArgumentException If the request targets Surfaces not
- * currently configured as outputs. Or if the handler is null, the listener
- * is not null, and the calling thread has no looper.
- *
- * @see #captureBurst
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
- * @deprecated Use {@link CameraCaptureSession} instead
+ * @deprecated Use {@link CameraCaptureSession#capture} instead
*/
- public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
+ @Deprecated
+ public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler)
throws CameraAccessException;
/**
- * Submit a list of requests to be captured in sequence as a burst. The
- * burst will be captured in the minimum amount of time possible, and will
- * not be interleaved with requests submitted by other capture or repeat
- * calls.
+ * Submit a list of requests to be captured in sequence as a burst.
*
- * <p>The requests will be captured in order, each capture producing one
- * {@link CaptureResult} and image buffers for one or more target
- * {@link android.view.Surface surfaces}. The target surfaces for each
- * request (set with {@link CaptureRequest.Builder#addTarget}) must be
- * configured as active outputs with {@link #configureOutputs} before
- * calling this method.</p>
- *
- * <p>The main difference between this method and simply calling
- * {@link #capture} repeatedly is that this method guarantees that no
- * other requests will be interspersed with the burst.</p>
- *
- * @param requests the list of settings for this burst capture
- * @param listener The callback object to notify each time one of the
- * requests in the burst has been processed. If null, no metadata will be
- * produced for any requests in this burst, although image data will still
- * be produced.
- * @param handler the handler on which the listener should be invoked, or
- * {@code null} to use the current thread's {@link android.os.Looper
- * looper}.
- *
- * @return int A unique capture sequence ID used by
- * {@link CaptureListener#onCaptureSequenceCompleted}.
- *
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera is currently busy or unconfigured,
- * or the camera device has been closed.
- * @throws IllegalArgumentException If the requests target Surfaces not
- * currently configured as outputs. Or if the handler is null, the listener
- * is not null, and the calling thread has no looper.
- *
- * @see #capture
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
- * @deprecated Use {@link CameraCaptureSession} instead
+ * @deprecated Use {@link CameraCaptureSession#captureBurst} instead
*/
- public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+ @Deprecated
+ public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException;
/**
* Request endlessly repeating capture of images by this CameraDevice.
*
- * <p>With this method, the CameraDevice will continually capture images
- * using the settings in the provided {@link CaptureRequest}, at the maximum
- * rate possible.</p>
- *
- * <p>Repeating requests are a simple way for an application to maintain a
- * preview or other continuous stream of frames, without having to
- * continually submit identical requests through {@link #capture}.</p>
- *
- * <p>Repeat requests have lower priority than those submitted
- * through {@link #capture} or {@link #captureBurst}, so if
- * {@link #capture} is called when a repeating request is active, the
- * capture request will be processed before any further repeating
- * requests are processed.<p>
- *
- * <p>Repeating requests are a simple way for an application to maintain a
- * preview or other continuous stream of frames, without having to submit
- * requests through {@link #capture} at video rates.</p>
- *
- * <p>To stop the repeating capture, call {@link #stopRepeating}. Calling
- * {@link #flush} will also clear the request.</p>
- *
- * <p>Calling this method will replace any earlier repeating request or
- * burst set up by this method or {@link #setRepeatingBurst}, although any
- * in-progress burst will be completed before the new repeat request will be
- * used.</p>
- *
- * @param request the request to repeat indefinitely
- * @param listener The callback object to notify every time the
- * request finishes processing. If null, no metadata will be
- * produced for this stream of requests, although image data will
- * still be produced.
- * @param handler the handler on which the listener should be invoked, or
- * {@code null} to use the current thread's {@link android.os.Looper
- * looper}.
- *
- * @return int A unique capture sequence ID used by
- * {@link CaptureListener#onCaptureSequenceCompleted}.
- *
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera is currently busy or unconfigured,
- * or the camera device has been closed.
- * @throws IllegalArgumentException If the requests reference Surfaces not
- * currently configured as outputs. Or if the handler is null, the listener
- * is not null, and the calling thread has no looper.
- *
- * @see #capture
- * @see #captureBurst
- * @see #setRepeatingBurst
- * @see #stopRepeating
- * @see #flush
- * @deprecated Use {@link CameraCaptureSession} instead
+ * @deprecated Use {@link CameraCaptureSession#setRepeatingRequest} instead
*/
- public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+ @Deprecated
+ public abstract int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
Handler handler) throws CameraAccessException;
/**
* <p>Request endlessly repeating capture of a sequence of images by this
* CameraDevice.</p>
*
- * <p>With this method, the CameraDevice will continually capture images,
- * cycling through the settings in the provided list of
- * {@link CaptureRequest CaptureRequests}, at the maximum rate possible.</p>
- *
- * <p>If a request is submitted through {@link #capture} or
- * {@link #captureBurst}, the current repetition of the request list will be
- * completed before the higher-priority request is handled. This guarantees
- * that the application always receives a complete repeat burst captured in
- * minimal time, instead of bursts interleaved with higher-priority
- * captures, or incomplete captures.</p>
- *
- * <p>Repeating burst requests are a simple way for an application to
- * maintain a preview or other continuous stream of frames where each
- * request is different in a predicatable way, without having to continually
- * submit requests through {@link #captureBurst} .</p>
- *
- * <p>To stop the repeating capture, call {@link #stopRepeating}. Any
- * ongoing burst will still be completed, however. Calling
- * {@link #flush} will also clear the request.</p>
- *
- * <p>Calling this method will replace a previously-set repeating request or
- * burst set up by this method or {@link #setRepeatingRequest}, although any
- * in-progress burst will be completed before the new repeat burst will be
- * used.</p>
- *
- * @param requests the list of requests to cycle through indefinitely
- * @param listener The callback object to notify each time one of the
- * requests in the repeating bursts has finished processing. If null, no
- * metadata will be produced for this stream of requests, although image
- * data will still be produced.
- * @param handler the handler on which the listener should be invoked, or
- * {@code null} to use the current thread's {@link android.os.Looper
- * looper}.
- *
- * @return int A unique capture sequence ID used by
- * {@link CaptureListener#onCaptureSequenceCompleted}.
- *
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera is currently busy or unconfigured,
- * or the camera device has been closed.
- * @throws IllegalArgumentException If the requests reference Surfaces not
- * currently configured as outputs. Or if the handler is null, the listener
- * is not null, and the calling thread has no looper.
- *
- * @see #capture
- * @see #captureBurst
- * @see #setRepeatingRequest
- * @see #stopRepeating
- * @see #flush
- * @deprecated Use {@link CameraCaptureSession} instead
+ * @deprecated Use {@link CameraCaptureSession#setRepeatingBurst} instead
*/
- public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
+ @Deprecated
+ public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
Handler handler) throws CameraAccessException;
/**
* <p>Cancel any ongoing repeating capture set by either
* {@link #setRepeatingRequest setRepeatingRequest} or
- * {@link #setRepeatingBurst}. Has no effect on requests submitted through
- * {@link #capture capture} or {@link #captureBurst captureBurst}.</p>
+ * {@link #setRepeatingBurst}.
*
- * <p>Any currently in-flight captures will still complete, as will any
- * burst that is mid-capture. To ensure that the device has finished
- * processing all of its capture requests and is in idle state, wait for the
- * {@link StateListener#onIdle} callback after calling this
- * method..</p>
- *
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera is currently busy or unconfigured,
- * or the camera device has been closed.
- *
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
- * @see StateListener#onIdle
- * @deprecated Use {@link CameraCaptureSession} instead
+ * @deprecated Use {@link CameraCaptureSession#stopRepeating} instead
*/
- public void stopRepeating() throws CameraAccessException;
+ @Deprecated
+ public abstract void stopRepeating() throws CameraAccessException;
/**
* Flush all captures currently pending and in-progress as fast as
* possible.
*
- * <p>The camera device will discard all of its current work as fast as
- * possible. Some in-flight captures may complete successfully and call
- * {@link CaptureListener#onCaptureCompleted}, while others will trigger
- * their {@link CaptureListener#onCaptureFailed} callbacks. If a repeating
- * request or a repeating burst is set, it will be cleared by the flush.</p>
- *
- * <p>This method is the fastest way to idle the camera device for
- * reconfiguration with {@link #configureOutputs}, at the cost of discarding
- * in-progress work. Once the flush is complete, the idle callback will be
- * called.</p>
- *
- * <p>Flushing will introduce at least a brief pause in the stream of data
- * from the camera device, since once the flush is complete, the first new
- * request has to make it through the entire camera pipeline before new
- * output buffers are produced.</p>
- *
- * <p>This means that using {@code flush()} to simply remove pending
- * requests is not recommended; it's best used for quickly switching output
- * configurations, or for cancelling long in-progress requests (such as a
- * multi-second capture).</p>
- *
- * @throws CameraAccessException if the camera device is no longer connected or has
- * encountered a fatal error
- * @throws IllegalStateException if the camera is not idle/active,
- * or the camera device has been closed.
- *
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
- * @see #configureOutputs
- * @deprecated Use {@link CameraCaptureSession} instead
+ * @deprecated Use {@link CameraCaptureSession#abortCaptures} instead
*/
- public void flush() throws CameraAccessException;
+ @Deprecated
+ public abstract void flush() throws CameraAccessException;
/**
* Close the connection to this camera device as quickly as possible.
@@ -675,22 +346,15 @@
*
*/
@Override
- public void close();
+ public abstract void close();
/**
* <p>A listener for tracking the progress of a {@link CaptureRequest}
* submitted to the camera device.</p>
*
- * <p>This listener is called when a request triggers a capture to start,
- * and when the capture is complete. In case on an error capturing an image,
- * the error method is triggered instead of the completion method.</p>
- *
- * @see #capture
- * @see #captureBurst
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
- * @deprecated Use {@link CameraCaptureSession} instead
+ * @deprecated Use {@link CameraCaptureSession.CaptureListener} instead
*/
+ @Deprecated
public static abstract class CaptureListener {
/**
@@ -705,29 +369,6 @@
* This method is called when the camera device has started capturing
* the output image for the request, at the beginning of image exposure.
*
- * <p>This callback is invoked right as the capture of a frame begins,
- * so it is the most appropriate time for playing a shutter sound,
- * or triggering UI indicators of capture.</p>
- *
- * <p>The request that is being used for this capture is provided, along
- * with the actual timestamp for the start of exposure. This timestamp
- * matches the timestamp that will be included in
- * {@link CaptureResult#SENSOR_TIMESTAMP the result timestamp field},
- * and in the buffers sent to each output Surface. These buffer
- * timestamps are accessible through, for example,
- * {@link android.media.Image#getTimestamp() Image.getTimestamp()} or
- * {@link android.graphics.SurfaceTexture#getTimestamp()}.</p>
- *
- * <p>For the simplest way to play a shutter sound camera shutter or a
- * video recording start/stop sound, see the
- * {@link android.media.MediaActionSound} class.</p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera the CameraDevice sending the callback
- * @param request the request for the capture that just begun
- * @param timestamp the timestamp at start of capture, in nanoseconds.
- *
* @see android.media.MediaActionSound
*/
public void onCaptureStarted(CameraDevice camera,
@@ -739,25 +380,6 @@
* This method is called when some results from an image capture are
* available.
*
- * <p>The result provided here will contain some subset of the fields of
- * a full result. Multiple onCapturePartial calls may happen per
- * capture; a given result field will only be present in one partial
- * capture at most. The final onCaptureCompleted call will always
- * contain all the fields, whether onCapturePartial was called or
- * not.</p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera The CameraDevice sending the callback.
- * @param request The request that was given to the CameraDevice
- * @param result The partial output metadata from the capture, which
- * includes a subset of the CaptureResult fields.
- *
- * @see #capture
- * @see #captureBurst
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
- *
* @hide
*/
public void onCapturePartial(CameraDevice camera,
@@ -769,37 +391,6 @@
* This method is called when an image capture makes partial forward progress; some
* (but not all) results from an image capture are available.
*
- * <p>The result provided here will contain some subset of the fields of
- * a full result. Multiple {@link #onCaptureProgressed} calls may happen per
- * capture; a given result field will only be present in one partial
- * capture at most. The final {@link #onCaptureCompleted} call will always
- * contain all the fields (in particular, the union of all the fields of all
- * the partial results composing the total result).</p>
- *
- * <p>For each request, some result data might be available earlier than others. The typical
- * delay between each partial result (per request) is a single frame interval.
- * For performance-oriented use-cases, applications should query the metadata they need
- * to make forward progress from the partial results and avoid waiting for the completed
- * result.</p>
- *
- * <p>Each request will generate at least {@code 1} partial results, and at most
- * {@link CameraCharacteristics#REQUEST_PARTIAL_RESULT_COUNT} partial results.</p>
- *
- * <p>Depending on the request settings, the number of partial results per request
- * will vary, although typically the partial count could be the same as long as the
- * camera device subsystems enabled stay the same.</p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera The CameraDevice sending the callback.
- * @param request The request that was given to the CameraDevice
- * @param partialResult The partial output metadata from the capture, which
- * includes a subset of the {@link TotalCaptureResult} fields.
- *
- * @see #capture
- * @see #captureBurst
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
*/
public void onCaptureProgressed(CameraDevice camera,
CaptureRequest request, CaptureResult partialResult) {
@@ -809,26 +400,6 @@
/**
* This method is called when an image capture has fully completed and all the
* result metadata is available.
- *
- * <p>This callback will always fire after the last {@link #onCaptureProgressed};
- * in other words, no more partial results will be delivered once the completed result
- * is available.</p>
- *
- * <p>For performance-intensive use-cases where latency is a factor, consider
- * using {@link #onCaptureProgressed} instead.</p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera The CameraDevice sending the callback.
- * @param request The request that was given to the CameraDevice
- * @param result The total output metadata from the capture, including the
- * final capture parameters and the state of the camera system during
- * capture.
- *
- * @see #capture
- * @see #captureBurst
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
*/
public void onCaptureCompleted(CameraDevice camera,
CaptureRequest request, TotalCaptureResult result) {
@@ -839,29 +410,6 @@
* This method is called instead of {@link #onCaptureCompleted} when the
* camera device failed to produce a {@link CaptureResult} for the
* request.
- *
- * <p>Other requests are unaffected, and some or all image buffers from
- * the capture may have been pushed to their respective output
- * streams.</p>
- *
- * <p>Some partial results may have been delivered before the capture fails;
- * however after this callback fires, no more partial results will be delivered by
- * {@link #onCaptureProgressed}.</p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera
- * The CameraDevice sending the callback.
- * @param request
- * The request that was given to the CameraDevice
- * @param failure
- * The output failure from the capture, including the failure reason
- * and the frame number.
- *
- * @see #capture
- * @see #captureBurst
- * @see #setRepeatingRequest
- * @see #setRepeatingBurst
*/
public void onCaptureFailed(CameraDevice camera,
CaptureRequest request, CaptureFailure failure) {
@@ -872,26 +420,6 @@
* This method is called independently of the others in CaptureListener,
* when a capture sequence finishes and all {@link CaptureResult}
* or {@link CaptureFailure} for it have been returned via this listener.
- *
- * <p>In total, there will be at least one result/failure returned by this listener
- * before this callback is invoked. If the capture sequence is aborted before any
- * requests have been processed, {@link #onCaptureSequenceAborted} is invoked instead.</p>
- *
- * <p>The default implementation does nothing.</p>
- *
- * @param camera
- * The CameraDevice sending the callback.
- * @param sequenceId
- * A sequence ID returned by the {@link #capture} family of functions.
- * @param frameNumber
- * The last frame number (returned by {@link CaptureResult#getFrameNumber}
- * or {@link CaptureFailure#getFrameNumber}) in the capture sequence.
- *
- * @see CaptureResult#getFrameNumber()
- * @see CaptureFailure#getFrameNumber()
- * @see CaptureResult#getSequenceId()
- * @see CaptureFailure#getSequenceId()
- * @see #onCaptureSequenceAborted
*/
public void onCaptureSequenceCompleted(CameraDevice camera,
int sequenceId, long frameNumber) {
@@ -902,25 +430,6 @@
* This method is called independently of the others in CaptureListener,
* when a capture sequence aborts before any {@link CaptureResult}
* or {@link CaptureFailure} for it have been returned via this listener.
- *
- * <p>Due to the asynchronous nature of the camera device, not all submitted captures
- * are immediately processed. It is possible to clear out the pending requests
- * by a variety of operations such as {@link CameraDevice#stopRepeating} or
- * {@link CameraDevice#flush}. When such an event happens,
- * {@link #onCaptureSequenceCompleted} will not be called.</p>
- *
- * <p>The default implementation does nothing.</p>
- *
- * @param camera
- * The CameraDevice sending the callback.
- * @param sequenceId
- * A sequence ID returned by the {@link #capture} family of functions.
- *
- * @see CaptureResult#getFrameNumber()
- * @see CaptureFailure#getFrameNumber()
- * @see CaptureResult#getSequenceId()
- * @see CaptureFailure#getSequenceId()
- * @see #onCaptureSequenceCompleted
*/
public void onCaptureSequenceAborted(CameraDevice camera,
int sequenceId) {
@@ -935,14 +444,14 @@
* <p>A listener must be provided to the {@link CameraManager#openCamera}
* method to open a camera device.</p>
*
- * <p>These events include notifications about the device becoming idle (
- * allowing for {@link #configureOutputs} to be called), about device
- * disconnection, and about unexpected device errors.</p>
+ * <p>These events include notifications about the device completing startup (
+ * allowing for {@link #createCaptureSession} to be called), about device
+ * disconnection or closure, and about unexpected device errors.</p>
*
- * <p>Events about the progress of specific {@link CaptureRequest
- * CaptureRequests} are provided through a {@link CaptureListener} given to
- * the {@link #capture}, {@link #captureBurst}, {@link
- * #setRepeatingRequest}, or {@link #setRepeatingBurst} methods.
+ * <p>Events about the progress of specific {@link CaptureRequest CaptureRequests} are provided
+ * through a {@link CameraCaptureSession.CaptureListener} given to the
+ * {@link CameraCaptureSession#capture}, {@link CameraCaptureSession#captureBurst},
+ * {@link CameraCaptureSession#setRepeatingRequest}, or {@link CameraCaptureSession#setRepeatingBurst} methods.
*
* @see CameraManager#openCamera
*/
@@ -1016,8 +525,9 @@
/**
* The method called when a camera device has finished opening.
*
- * <p>An opened camera will immediately afterwards transition into
- * {@link #onUnconfigured}.</p>
+ * <p>At this point, the camera device is ready to use, and
+ * {@link CameraDevice#createCaptureSession} can be called to set up the first capture
+ * session.</p>
*
* @param camera the camera device that has become opened
*/
@@ -1026,22 +536,9 @@
/**
* The method called when a camera device has no outputs configured.
*
- * <p>An unconfigured camera device needs to be configured with
- * {@link CameraDevice#configureOutputs} before being able to
- * submit any capture request.</p>
- *
- * <p>This state may be entered by a newly opened camera or by
- * calling {@link CameraDevice#configureOutputs} with a null/empty
- * list of Surfaces when idle.</p>
- *
- * <p>Any attempts to submit a capture request while in this state
- * will result in an {@link IllegalStateException} being thrown.</p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera the camera device has that become unconfigured
- * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
+ * @deprecated Use {@link #onOpened} instead.
*/
+ @Deprecated
public void onUnconfigured(CameraDevice camera) {
// Default empty implementation
}
@@ -1050,28 +547,9 @@
* The method called when a camera device begins processing
* {@link CaptureRequest capture requests}.
*
- * <p>A camera may not be re-configured while in this state. The camera
- * will transition to the idle state once all pending captures have
- * completed. If a repeating request is set, the camera will remain active
- * until it is cleared and the remaining requests finish processing. To
- * transition to the idle state as quickly as possible, call {@link #flush()},
- * which will idle the camera device as quickly as possible, likely canceling
- * most in-progress captures.</p>
- *
- * <p>All calls except for {@link CameraDevice#configureOutputs} are
- * legal while in this state.
- * </p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera the camera device that has become active
- *
- * @see CameraDevice#capture
- * @see CameraDevice#captureBurst
- * @see CameraDevice#setRepeatingBurst
- * @see CameraDevice#setRepeatingRequest
- * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
+ * @deprecated Use {@link CameraCaptureSession.StateListener#onActive} instead.
*/
+ @Deprecated
public void onActive(CameraDevice camera) {
// Default empty implementation
}
@@ -1079,33 +557,9 @@
/**
* The method called when a camera device is busy.
*
- * <p>A camera becomes busy while it's outputs are being configured
- * (after a call to {@link CameraDevice#configureOutputs} or while it's
- * being flushed (after a call to {@link CameraDevice#flush}.</p>
- *
- * <p>Once the on-going operations are complete, the camera will automatically
- * transition into {@link #onIdle} if there is at least one configured output,
- * or {@link #onUnconfigured} otherwise.</p>
- *
- * <p>Any attempts to manipulate the camera while its is busy
- * will result in an {@link IllegalStateException} being thrown.</p>
- *
- * <p>Only the following methods are valid to call while in this state:
- * <ul>
- * <li>{@link CameraDevice#getId}</li>
- * <li>{@link CameraDevice#createCaptureRequest}</li>
- * <li>{@link CameraDevice#close}</li>
- * </ul>
- * </p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera the camera device that has become busy
- *
- * @see CameraDevice#configureOutputs
- * @see CameraDevice#flush
- * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
+ * @deprecated Use {@link CameraCaptureSession.StateListener#onConfigured} instead.
*/
+ @Deprecated
public void onBusy(CameraDevice camera) {
// Default empty implementation
}
@@ -1129,31 +583,9 @@
* The method called when a camera device has finished processing all
* submitted capture requests and has reached an idle state.
*
- * <p>An idle camera device can have its outputs changed by calling {@link
- * CameraDevice#configureOutputs}, which will transition it into the busy state.</p>
- *
- * <p>To idle and reconfigure outputs without canceling any submitted
- * capture requests, the application needs to clear its repeating
- * request/burst, if set, with {@link CameraDevice#stopRepeating}, and
- * then wait for this callback to be called before calling {@link
- * CameraDevice#configureOutputs}.</p>
- *
- * <p>To idle and reconfigure a camera device as fast as possible, the
- * {@link CameraDevice#flush} method can be used, which will discard all
- * pending and in-progress capture requests. Once the {@link
- * CameraDevice#flush} method is called, the application must wait for
- * this callback to fire before calling {@link
- * CameraDevice#configureOutputs}.</p>
- *
- * <p>The default implementation of this method does nothing.</p>
- *
- * @param camera the camera device that has become idle
- *
- * @see CameraDevice#configureOutputs
- * @see CameraDevice#stopRepeating
- * @see CameraDevice#flush
- * @deprecated Use {@link CameraCaptureSession.StateListener} instead.
+ * @deprecated Use {@link CameraCaptureSession.StateListener#onReady} instead.
*/
+ @Deprecated
public void onIdle(CameraDevice camera) {
// Default empty implementation
}
@@ -1207,7 +639,7 @@
*
* @param camera The device reporting the error
* @param error The error code, one of the
- * {@code CameraDeviceListener.ERROR_*} values.
+ * {@code StateListener.ERROR_*} values.
*
* @see #ERROR_CAMERA_DEVICE
* @see #ERROR_CAMERA_SERVICE
@@ -1216,4 +648,10 @@
*/
public abstract void onError(CameraDevice camera, int error); // Must implement
}
+
+ /**
+ * To be inherited by android.hardware.camera2.* code only.
+ * @hide
+ */
+ public CameraDevice() {}
}
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 4a89fe7..0901562 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -35,7 +35,7 @@
import java.util.ArrayList;
/**
- * <p>An interface for iterating, listing, and connecting to
+ * <p>A system service manager for detecting, characterizing, and connecting to
* {@link CameraDevice CameraDevices}.</p>
*
* <p>You can get an instance of this class by calling
@@ -225,10 +225,10 @@
synchronized (mLock) {
- ICameraDeviceUser cameraUser;
+ ICameraDeviceUser cameraUser = null;
- android.hardware.camera2.impl.CameraDevice deviceImpl =
- new android.hardware.camera2.impl.CameraDevice(
+ android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
+ new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
listener,
handler,
@@ -247,8 +247,23 @@
// Use legacy camera implementation for HAL1 devices
Log.i(TAG, "Using legacy camera HAL.");
cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
+ } else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
+ e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
+ e.getReason() == CameraAccessException.CAMERA_DISABLED ||
+ e.getReason() == CameraAccessException.CAMERA_DISCONNECTED ||
+ e.getReason() == CameraAccessException.CAMERA_ERROR) {
+ // Received one of the known connection errors
+ // The remote camera device cannot be connected to, so
+ // set the local camera to the startup error state
+ deviceImpl.setRemoteFailure(e);
+
+ if (e.getReason() == CameraAccessException.CAMERA_DISABLED ||
+ e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
+ // Per API docs, these failures call onError and throw
+ throw e;
+ }
} else {
- // Rethrow otherwise
+ // Unexpected failure - rethrow
throw e;
}
}
@@ -298,7 +313,7 @@
*
* <p>If opening the camera device fails, then the device listener's
* {@link CameraDevice.StateListener#onError onError} method will be called, and subsequent
- * calls on the camera device will throw an {@link IllegalStateException}.</p>
+ * calls on the camera device will throw a {@link CameraAccessException}.</p>
*
* @param cameraId
* The unique identifier of the camera device to open
@@ -342,14 +357,18 @@
}
/**
- * Interface for listening to camera devices becoming available or
- * unavailable.
+ * A listener for camera devices becoming available or
+ * unavailable to open.
*
* <p>Cameras become available when they are no longer in use, or when a new
* removable camera is connected. They become unavailable when some
* application or service starts using a camera, or when a removable camera
* is disconnected.</p>
*
+ * <p>Extend this listener and pass an instance of the subclass to
+ * {@link CameraManager#addAvailabilityListener} to be notified of such availability
+ * changes.</p>
+ *
* @see addAvailabilityListener
*/
public static abstract class AvailabilityListener {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index b3e165e..33e1915 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -157,8 +157,8 @@
/**
* <p>The lens focus distance is not accurate, and the units used for
- * {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} do not correspond to any physical units.
- * Setting the lens to the same focus distance on separate occasions may
+ * {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} do not correspond to any physical units.</p>
+ * <p>Setting the lens to the same focus distance on separate occasions may
* result in a different real focus distance, depending on factors such
* as the orientation of the device, the age of the focusing mechanism,
* and the device temperature. The focus distance value will still be
@@ -172,20 +172,24 @@
public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED = 0;
/**
- * <p>The lens focus distance is measured in diopters. However, setting the lens
- * to the same focus distance on separate occasions may result in a
- * different real focus distance, depending on factors such as the
- * orientation of the device, the age of the focusing mechanism, and
- * the device temperature.</p>
+ * <p>The lens focus distance is measured in diopters.</p>
+ * <p>However, setting the lens to the same focus distance
+ * on separate occasions may result in a different real
+ * focus distance, depending on factors such as the
+ * orientation of the device, the age of the focusing
+ * mechanism, and the device temperature.</p>
* @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION
*/
public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE = 1;
/**
- * <p>The lens focus distance is measured in diopters. The lens mechanism is
- * calibrated so that setting the same focus distance is repeatable on
- * multiple occasions with good accuracy, and the focus distance corresponds
- * to the real physical distance to the plane of best focus.</p>
+ * <p>The lens focus distance is measured in diopters, and
+ * is calibrated.</p>
+ * <p>The lens mechanism is calibrated so that setting the
+ * same focus distance is repeatable on multiple
+ * occasions with good accuracy, and the focus distance
+ * corresponds to the real physical distance to the plane
+ * of best focus.</p>
* @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION
*/
public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED = 2;
@@ -195,11 +199,13 @@
//
/**
+ * <p>The camera device faces the same direction as the device's screen.</p>
* @see CameraCharacteristics#LENS_FACING
*/
public static final int LENS_FACING_FRONT = 0;
/**
+ * <p>The camera device faces the opposite direction as the device's screen.</p>
* @see CameraCharacteristics#LENS_FACING
*/
public static final int LENS_FACING_BACK = 1;
@@ -215,11 +221,10 @@
* <p>The full set of features supported by this capability makes
* the camera2 api backwards compatible with the camera1
* (android.hardware.Camera) API.</p>
- * <p>TODO: @hide this. Doesn't really mean anything except
- * act as a catch-all for all the 'base' functionality.</p>
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @hide
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0;
@@ -228,15 +233,14 @@
* tags or functionality not encapsulated by one of the other
* capabilities.</p>
* <p>A typical example is all tags marked 'optional'.</p>
- * <p>TODO: @hide. We may not need this if we @hide all the optional
- * tags not belonging to a capability.</p>
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @hide
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_OPTIONAL = 1;
/**
* <p>The camera device can be manually controlled (3A algorithms such
- * as auto exposure, and auto focus can be bypassed).
+ * as auto-exposure, and auto-focus can be bypassed).
* The camera device supports basic manual control of the sensor image
* acquisition related stages. This means the following controls are
* guaranteed to be supported:</p>
@@ -257,11 +261,11 @@
* <li>{@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}</li>
* </ul>
* </li>
- * <li>Manual lens control<ul>
+ * <li>Manual lens control (if the lens is adjustable)<ul>
* <li>android.lens.*</li>
* </ul>
* </li>
- * <li>Manual flash control<ul>
+ * <li>Manual flash control (if a flash unit is present)<ul>
* <li>android.flash.*</li>
* </ul>
* </li>
@@ -312,8 +316,6 @@
* </ul>
* <p>If auto white balance is enabled, then the camera device
* will accurately report the values applied by AWB in the result.</p>
- * <p>The camera device will also support everything in MANUAL_SENSOR
- * except manual lens control and manual flash control.</p>
* <p>A given camera device may also support additional post-processing
* controls, but this capability only covers the above list of controls.</p>
*
@@ -341,6 +343,7 @@
* resolution of JPEG streams.</li>
* </ul>
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @hide
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4;
@@ -354,12 +357,14 @@
* <li>RAW16 is reprocessable into both YUV_420_888 and JPEG
* formats.</li>
* <li>The maximum available resolution for RAW16 streams (both
- * input/output) will match the value in
- * {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize}.</li>
+ * input/output) will match either the value in
+ * {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize} or
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</li>
* <li>All DNG-related optional metadata entries are provided
* by the camera device.</li>
* </ul>
*
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
@@ -370,13 +375,13 @@
//
/**
- * <p>The camera device will only support centered crop regions.</p>
+ * <p>The camera device only supports centered crop regions.</p>
* @see CameraCharacteristics#SCALER_CROPPING_TYPE
*/
public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0;
/**
- * <p>The camera device will support arbitrarily chosen crop regions.</p>
+ * <p>The camera device supports arbitrarily chosen crop regions.</p>
* @see CameraCharacteristics#SCALER_CROPPING_TYPE
*/
public static final int SCALER_CROPPING_TYPE_FREEFORM = 1;
@@ -522,7 +527,7 @@
//
/**
- * <p>android.led.transmit control is used</p>
+ * <p>android.led.transmit control is used.</p>
* @see CameraCharacteristics#LED_AVAILABLE_LEDS
* @hide
*/
@@ -533,11 +538,14 @@
//
/**
+ * <p>This camera device has only limited capabilities.</p>
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0;
/**
+ * <p>This camera device is capable of supporting advanced imaging
+ * applications.</p>
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
*/
public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1;
@@ -547,9 +555,9 @@
//
/**
- * <p>Every frame has the requests immediately applied.
- * (and furthermore for all results,
- * <code>android.sync.frameNumber == android.request.frameCount</code>)</p>
+ * <p>Every frame has the requests immediately applied.</p>
+ * <p>Furthermore for all results,
+ * <code>android.sync.frameNumber == android.request.frameCount</code></p>
* <p>Changing controls over multiple requests one after another will
* produce results that have those controls applied atomically
* each frame.</p>
@@ -589,8 +597,8 @@
public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0;
/**
- * <p>Must not slow down capture rate relative to sensor raw
- * output.</p>
+ * <p>Color correction processing must not slow down
+ * capture rate relative to sensor raw output.</p>
* <p>Advanced white balance adjustments above and beyond
* the specified white balance pipeline may be applied.</p>
* <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then
@@ -603,8 +611,9 @@
public static final int COLOR_CORRECTION_MODE_FAST = 1;
/**
- * <p>Capture rate (relative to sensor raw output)
- * may be reduced by high quality.</p>
+ * <p>Color correction processing operates at improved
+ * quality but reduced capture rate (relative to sensor raw
+ * output).</p>
* <p>Advanced white balance adjustments above and beyond
* the specified white balance pipeline may be applied.</p>
* <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then
@@ -655,8 +664,8 @@
//
/**
- * <p>The camera device's autoexposure routine is disabled;
- * the application-selected {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime},
+ * <p>The camera device's autoexposure routine is disabled.</p>
+ * <p>The application-selected {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime},
* {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are used by the camera
* device, along with android.flash.* fields, if there's
@@ -671,7 +680,8 @@
/**
* <p>The camera device's autoexposure routine is active,
- * with no flash control. The application's values for
+ * with no flash control.</p>
+ * <p>The application's values for
* {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime},
* {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored. The
@@ -688,10 +698,10 @@
/**
* <p>Like ON, except that the camera device also controls
* the camera's flash unit, firing it in low-light
- * conditions. The flash may be fired during a
- * precapture sequence (triggered by
- * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and may be fired
- * for captures for which the
+ * conditions.</p>
+ * <p>The flash may be fired during a precapture sequence
+ * (triggered by {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and
+ * may be fired for captures for which the
* {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} field is set to
* STILL_CAPTURE</p>
*
@@ -704,10 +714,10 @@
/**
* <p>Like ON, except that the camera device also controls
* the camera's flash unit, always firing it for still
- * captures. The flash may be fired during a precapture
- * sequence (triggered by
- * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and will always
- * be fired for captures for which the
+ * captures.</p>
+ * <p>The flash may be fired during a precapture sequence
+ * (triggered by {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and
+ * will always be fired for captures for which the
* {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} field is set to
* STILL_CAPTURE</p>
*
@@ -719,9 +729,10 @@
/**
* <p>Like ON_AUTO_FLASH, but with automatic red eye
- * reduction. If deemed necessary by the camera device,
- * a red eye reduction flash will fire during the
- * precapture sequence.</p>
+ * reduction.</p>
+ * <p>If deemed necessary by the camera device, a red eye
+ * reduction flash will fire during the precapture
+ * sequence.</p>
* @see CaptureRequest#CONTROL_AE_MODE
*/
public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4;
@@ -738,8 +749,9 @@
/**
* <p>The precapture metering sequence will be started
- * by the camera device. The exact effect of the precapture
- * trigger depends on the current AE mode and state.</p>
+ * by the camera device.</p>
+ * <p>The exact effect of the precapture trigger depends on
+ * the current AE mode and state.</p>
* @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
*/
public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1;
@@ -751,7 +763,7 @@
/**
* <p>The auto-focus routine does not control the lens;
* {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} is controlled by the
- * application</p>
+ * application.</p>
*
* @see CaptureRequest#LENS_FOCUS_DISTANCE
* @see CaptureRequest#CONTROL_AF_MODE
@@ -759,12 +771,14 @@
public static final int CONTROL_AF_MODE_OFF = 0;
/**
- * <p>If lens is not fixed focus.</p>
- * <p>Use {@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} to determine if lens
- * is fixed-focus. In this mode, the lens does not move unless
+ * <p>Basic automatic focus mode.</p>
+ * <p>In this mode, the lens does not move unless
* the autofocus trigger action is called. When that trigger
- * is activated, AF must transition to ACTIVE_SCAN, then to
+ * is activated, AF will transition to ACTIVE_SCAN, then to
* the outcome of the scan (FOCUSED or NOT_FOCUSED).</p>
+ * <p>Always supported if lens is not fixed focus.</p>
+ * <p>Use {@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} to determine if lens
+ * is fixed-focus.</p>
* <p>Triggering AF_CANCEL resets the lens position to default,
* and sets the AF state to INACTIVE.</p>
*
@@ -774,11 +788,16 @@
public static final int CONTROL_AF_MODE_AUTO = 1;
/**
+ * <p>Close-up focusing mode.</p>
* <p>In this mode, the lens does not move unless the
- * autofocus trigger action is called.</p>
- * <p>When that trigger is activated, AF must transition to
+ * autofocus trigger action is called. When that trigger is
+ * activated, AF will transition to ACTIVE_SCAN, then to
+ * the outcome of the scan (FOCUSED or NOT_FOCUSED). This
+ * mode is optimized for focusing on objects very close to
+ * the camera.</p>
+ * <p>When that trigger is activated, AF will transition to
* ACTIVE_SCAN, then to the outcome of the scan (FOCUSED or
- * NOT_FOCUSED). Triggering cancel AF resets the lens
+ * NOT_FOCUSED). Triggering cancel AF resets the lens
* position to default, and sets the AF state to
* INACTIVE.</p>
* @see CaptureRequest#CONTROL_AF_MODE
@@ -829,8 +848,11 @@
public static final int CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4;
/**
- * <p>Extended depth of field (digital focus). AF
- * trigger is ignored, AF state should always be
+ * <p>Extended depth of field (digital focus) mode.</p>
+ * <p>The camera device will produce images with an extended
+ * depth of field automatically; no special focusing
+ * operations need to be done before taking a picture.</p>
+ * <p>AF triggers are ignored, and the AF state will always be
* INACTIVE.</p>
* @see CaptureRequest#CONTROL_AF_MODE
*/
@@ -864,8 +886,8 @@
//
/**
- * <p>The camera device's auto white balance routine is disabled;
- * the application-selected color transform matrix
+ * <p>The camera device's auto-white balance routine is disabled.</p>
+ * <p>The application-selected color transform matrix
* ({@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}) and gains
* ({@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}) are used by the camera
* device for manual white balance control.</p>
@@ -877,9 +899,12 @@
public static final int CONTROL_AWB_MODE_OFF = 0;
/**
- * <p>The camera device's auto white balance routine is active;
- * the application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
- * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.</p>
+ * <p>The camera device's auto-white balance routine is active.</p>
+ * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
+ * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.
+ * For devices that support the MANUAL_POST_PROCESSING capability, the
+ * values used by the camera device for the transform and gains
+ * will be available in the capture result for this request.</p>
*
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
@@ -888,65 +913,125 @@
public static final int CONTROL_AWB_MODE_AUTO = 1;
/**
- * <p>The camera device's auto white balance routine is disabled;
+ * <p>The camera device's auto-white balance routine is disabled;
* the camera device uses incandescent light as the assumed scene
- * illumination for white balance. While the exact white balance
- * transforms are up to the camera device, they will approximately
- * match the CIE standard illuminant A.</p>
+ * illumination for white balance.</p>
+ * <p>While the exact white balance transforms are up to the
+ * camera device, they will approximately match the CIE
+ * standard illuminant A.</p>
+ * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
+ * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.
+ * For devices that support the MANUAL_POST_PROCESSING capability, the
+ * values used by the camera device for the transform and gains
+ * will be available in the capture result for this request.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_GAINS
+ * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final int CONTROL_AWB_MODE_INCANDESCENT = 2;
/**
- * <p>The camera device's auto white balance routine is disabled;
+ * <p>The camera device's auto-white balance routine is disabled;
* the camera device uses fluorescent light as the assumed scene
- * illumination for white balance. While the exact white balance
- * transforms are up to the camera device, they will approximately
- * match the CIE standard illuminant F2.</p>
+ * illumination for white balance.</p>
+ * <p>While the exact white balance transforms are up to the
+ * camera device, they will approximately match the CIE
+ * standard illuminant F2.</p>
+ * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
+ * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.
+ * For devices that support the MANUAL_POST_PROCESSING capability, the
+ * values used by the camera device for the transform and gains
+ * will be available in the capture result for this request.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_GAINS
+ * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final int CONTROL_AWB_MODE_FLUORESCENT = 3;
/**
- * <p>The camera device's auto white balance routine is disabled;
+ * <p>The camera device's auto-white balance routine is disabled;
* the camera device uses warm fluorescent light as the assumed scene
- * illumination for white balance. While the exact white balance
- * transforms are up to the camera device, they will approximately
- * match the CIE standard illuminant F4.</p>
+ * illumination for white balance.</p>
+ * <p>While the exact white balance transforms are up to the
+ * camera device, they will approximately match the CIE
+ * standard illuminant F4.</p>
+ * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
+ * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.
+ * For devices that support the MANUAL_POST_PROCESSING capability, the
+ * values used by the camera device for the transform and gains
+ * will be available in the capture result for this request.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_GAINS
+ * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final int CONTROL_AWB_MODE_WARM_FLUORESCENT = 4;
/**
- * <p>The camera device's auto white balance routine is disabled;
+ * <p>The camera device's auto-white balance routine is disabled;
* the camera device uses daylight light as the assumed scene
- * illumination for white balance. While the exact white balance
- * transforms are up to the camera device, they will approximately
- * match the CIE standard illuminant D65.</p>
+ * illumination for white balance.</p>
+ * <p>While the exact white balance transforms are up to the
+ * camera device, they will approximately match the CIE
+ * standard illuminant D65.</p>
+ * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
+ * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.
+ * For devices that support the MANUAL_POST_PROCESSING capability, the
+ * values used by the camera device for the transform and gains
+ * will be available in the capture result for this request.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_GAINS
+ * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final int CONTROL_AWB_MODE_DAYLIGHT = 5;
/**
- * <p>The camera device's auto white balance routine is disabled;
+ * <p>The camera device's auto-white balance routine is disabled;
* the camera device uses cloudy daylight light as the assumed scene
* illumination for white balance.</p>
+ * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
+ * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.
+ * For devices that support the MANUAL_POST_PROCESSING capability, the
+ * values used by the camera device for the transform and gains
+ * will be available in the capture result for this request.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_GAINS
+ * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final int CONTROL_AWB_MODE_CLOUDY_DAYLIGHT = 6;
/**
- * <p>The camera device's auto white balance routine is disabled;
+ * <p>The camera device's auto-white balance routine is disabled;
* the camera device uses twilight light as the assumed scene
* illumination for white balance.</p>
+ * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
+ * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.
+ * For devices that support the MANUAL_POST_PROCESSING capability, the
+ * values used by the camera device for the transform and gains
+ * will be available in the capture result for this request.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_GAINS
+ * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final int CONTROL_AWB_MODE_TWILIGHT = 7;
/**
- * <p>The camera device's auto white balance routine is disabled;
+ * <p>The camera device's auto-white balance routine is disabled;
* the camera device uses shade light as the assumed scene
* illumination for white balance.</p>
+ * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}
+ * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.
+ * For devices that support the MANUAL_POST_PROCESSING capability, the
+ * values used by the camera device for the transform and gains
+ * will be available in the capture result for this request.</p>
+ *
+ * @see CaptureRequest#COLOR_CORRECTION_GAINS
+ * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM
* @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final int CONTROL_AWB_MODE_SHADE = 8;
@@ -956,38 +1041,43 @@
//
/**
- * <p>This request doesn't fall into the other
- * categories. Default to preview-like
+ * <p>The goal of this request doesn't fall into the other
+ * categories. The camera device will default to preview-like
* behavior.</p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_CUSTOM = 0;
/**
- * <p>This request is for a preview-like usecase. The
- * precapture trigger may be used to start off a metering
- * w/flash sequence</p>
+ * <p>This request is for a preview-like use case.</p>
+ * <p>The precapture trigger may be used to start off a metering
+ * w/flash sequence.</p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_PREVIEW = 1;
/**
* <p>This request is for a still capture-type
- * usecase.</p>
+ * use case.</p>
+ * <p>If the flash unit is under automatic control, it may fire as needed.</p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_STILL_CAPTURE = 2;
/**
* <p>This request is for a video recording
- * usecase.</p>
+ * use case.</p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_VIDEO_RECORD = 3;
/**
* <p>This request is for a video snapshot (still
- * image while recording video) usecase</p>
+ * image while recording video) use case.</p>
+ * <p>The camera device should take the highest-quality image
+ * possible (given the other settings) without disrupting the
+ * frame rate of video recording.<br />
+ * </p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4;
@@ -996,15 +1086,16 @@
* <p>This request is for a ZSL usecase; the
* application will stream full-resolution images and
* reprocess one or several later for a final
- * capture</p>
+ * capture.</p>
* @see CaptureRequest#CONTROL_CAPTURE_INTENT
*/
public static final int CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG = 5;
/**
* <p>This request is for manual capture use case where
- * the applications want to directly control the capture parameters
- * (e.g. {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} etc.).</p>
+ * the applications want to directly control the capture parameters.</p>
+ * <p>For example, the application may wish to manually control
+ * {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, etc.</p>
*
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
* @see CaptureRequest#SENSOR_SENSITIVITY
@@ -1024,7 +1115,8 @@
/**
* <p>A "monocolor" effect where the image is mapped into
- * a single color. This will typically be grayscale.</p>
+ * a single color.</p>
+ * <p>This will typically be grayscale.</p>
* @see CaptureRequest#CONTROL_EFFECT_MODE
*/
public static final int CONTROL_EFFECT_MODE_MONO = 1;
@@ -1084,31 +1176,42 @@
//
/**
- * <p>Full application control of pipeline. All 3A
- * routines are disabled, no other settings in
- * android.control.* have any effect</p>
+ * <p>Full application control of pipeline.</p>
+ * <p>All control by the device's metering and focusing (3A)
+ * routines is disabled, and no other settings in
+ * android.control.* have any effect, except that
+ * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} may be used by the camera
+ * device to select post-processing values for processing
+ * blocks that do not allow for manual control, or are not
+ * exposed by the camera API.</p>
+ * <p>However, the camera device's 3A routines may continue to
+ * collect statistics and update their internal state so that
+ * when control is switched to AUTO mode, good control values
+ * can be immediately applied.</p>
+ *
+ * @see CaptureRequest#CONTROL_CAPTURE_INTENT
* @see CaptureRequest#CONTROL_MODE
*/
public static final int CONTROL_MODE_OFF = 0;
/**
- * <p>Use settings for each individual 3A routine.
- * Manual control of capture parameters is disabled. All
+ * <p>Use settings for each individual 3A routine.</p>
+ * <p>Manual control of capture parameters is disabled. All
* controls in android.control.* besides sceneMode take
- * effect</p>
+ * effect.</p>
* @see CaptureRequest#CONTROL_MODE
*/
public static final int CONTROL_MODE_AUTO = 1;
/**
- * <p>Use specific scene mode. Enabling this disables
- * control.aeMode, control.awbMode and control.afMode
- * controls; the camera device will ignore those settings while
- * USE_SCENE_MODE is active (except for FACE_PRIORITY
- * scene mode). Other control entries are still active.
- * This setting can only be used if scene mode is supported
- * (i.e. {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes} contain some modes
- * other than DISABLED).</p>
+ * <p>Use a specific scene mode.</p>
+ * <p>Enabling this disables control.aeMode, control.awbMode and
+ * control.afMode controls; the camera device will ignore
+ * those settings while USE_SCENE_MODE is active (except for
+ * FACE_PRIORITY scene mode). Other control entries are still
+ * active. This setting can only be used if scene mode is
+ * supported (i.e. {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}
+ * contain some modes other than DISABLED).</p>
*
* @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES
* @see CaptureRequest#CONTROL_MODE
@@ -1118,7 +1221,12 @@
/**
* <p>Same as OFF mode, except that this capture will not be
* used by camera device background auto-exposure, auto-white balance and
- * auto-focus algorithms to update their statistics.</p>
+ * auto-focus algorithms (3A) to update their statistics.</p>
+ * <p>Specifically, the 3A routines are locked to the last
+ * values set from a request with AUTO, OFF, or
+ * USE_SCENE_MODE, and any statistics or state updates
+ * collected from manual captures with OFF_KEEP_STATE will be
+ * discarded by the camera device.</p>
* @see CaptureRequest#CONTROL_MODE
*/
public static final int CONTROL_MODE_OFF_KEEP_STATE = 3;
@@ -1136,8 +1244,9 @@
/**
* <p>If face detection support exists, use face
* detection data for auto-focus, auto-white balance, and
- * auto-exposure routines. If face detection statistics are
- * disabled (i.e. {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} is set to OFF),
+ * auto-exposure routines.</p>
+ * <p>If face detection statistics are disabled
+ * (i.e. {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} is set to OFF),
* this should still operate correctly (but will not return
* face detection statistics to the framework).</p>
* <p>Unlike the other scene modes, {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode},
@@ -1153,8 +1262,8 @@
public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1;
/**
- * <p>Optimized for photos of quickly moving objects.
- * Similar to SPORTS.</p>
+ * <p>Optimized for photos of quickly moving objects.</p>
+ * <p>Similar to SPORTS.</p>
* @see CaptureRequest#CONTROL_SCENE_MODE
*/
public static final int CONTROL_SCENE_MODE_ACTION = 2;
@@ -1223,8 +1332,8 @@
public static final int CONTROL_SCENE_MODE_FIREWORKS = 12;
/**
- * <p>Optimized for photos of quickly moving people.
- * Similar to ACTION.</p>
+ * <p>Optimized for photos of quickly moving people.</p>
+ * <p>Similar to ACTION.</p>
* @see CaptureRequest#CONTROL_SCENE_MODE
*/
public static final int CONTROL_SCENE_MODE_SPORTS = 13;
@@ -1256,11 +1365,13 @@
//
/**
+ * <p>Video stabilization is disabled.</p>
* @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
*/
public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0;
/**
+ * <p>Video stabilization is enabled.</p>
* @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
*/
public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1;
@@ -1270,21 +1381,20 @@
//
/**
- * <p>No edge enhancement is applied</p>
+ * <p>No edge enhancement is applied.</p>
* @see CaptureRequest#EDGE_MODE
*/
public static final int EDGE_MODE_OFF = 0;
/**
- * <p>Must not slow down frame rate relative to sensor
+ * <p>Apply edge enhancement at a quality level that does not slow down frame rate relative to sensor
* output</p>
* @see CaptureRequest#EDGE_MODE
*/
public static final int EDGE_MODE_FAST = 1;
/**
- * <p>Frame rate may be reduced by high
- * quality</p>
+ * <p>Apply high-quality edge enhancement, at a cost of reducing output frame rate.</p>
* @see CaptureRequest#EDGE_MODE
*/
public static final int EDGE_MODE_HIGH_QUALITY = 2;
@@ -1317,10 +1427,10 @@
//
/**
+ * <p>No hot pixel correction is applied.</p>
* <p>The frame rate must not be reduced relative to sensor raw output
* for this option.</p>
- * <p>No hot pixel correction is applied.
- * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p>
+ * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p>
*
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
* @see CaptureRequest#HOT_PIXEL_MODE
@@ -1328,10 +1438,9 @@
public static final int HOT_PIXEL_MODE_OFF = 0;
/**
- * <p>The frame rate must not be reduced relative to sensor raw output
- * for this option.</p>
- * <p>Hot pixel correction is applied.
- * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p>
+ * <p>Hot pixel correction is applied, without reducing frame
+ * rate relative to sensor raw output.</p>
+ * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p>
*
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
* @see CaptureRequest#HOT_PIXEL_MODE
@@ -1339,10 +1448,9 @@
public static final int HOT_PIXEL_MODE_FAST = 1;
/**
- * <p>The frame rate may be reduced relative to sensor raw output
- * for this option.</p>
- * <p>A high-quality hot pixel correction is applied.
- * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p>
+ * <p>High-quality hot pixel correction is applied, at a cost
+ * of reducing frame rate relative to sensor raw output.</p>
+ * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p>
*
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
* @see CaptureRequest#HOT_PIXEL_MODE
@@ -1370,21 +1478,21 @@
//
/**
- * <p>No noise reduction is applied</p>
+ * <p>No noise reduction is applied.</p>
* @see CaptureRequest#NOISE_REDUCTION_MODE
*/
public static final int NOISE_REDUCTION_MODE_OFF = 0;
/**
- * <p>Must not slow down frame rate relative to sensor
- * output</p>
+ * <p>Noise reduction is applied without reducing frame rate relative to sensor
+ * output.</p>
* @see CaptureRequest#NOISE_REDUCTION_MODE
*/
public static final int NOISE_REDUCTION_MODE_FAST = 1;
/**
- * <p>May slow down frame rate to provide highest
- * quality</p>
+ * <p>High-quality noise reduction is applied, at the cost of reducing frame rate
+ * relative to sensor output.</p>
* @see CaptureRequest#NOISE_REDUCTION_MODE
*/
public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2;
@@ -1394,8 +1502,9 @@
//
/**
- * <p>Default. No test pattern mode is used, and the camera
+ * <p>No test pattern mode is used, and the camera
* device returns captures from the image sensor.</p>
+ * <p>This is the default if the key is not set.</p>
* @see CaptureRequest#SENSOR_TEST_PATTERN_MODE
*/
public static final int SENSOR_TEST_PATTERN_MODE_OFF = 0;
@@ -1499,19 +1608,21 @@
//
/**
- * <p>No lens shading correction is applied</p>
+ * <p>No lens shading correction is applied.</p>
* @see CaptureRequest#SHADING_MODE
*/
public static final int SHADING_MODE_OFF = 0;
/**
- * <p>Must not slow down frame rate relative to sensor raw output</p>
+ * <p>Apply lens shading corrections, without slowing
+ * frame rate relative to sensor raw output</p>
* @see CaptureRequest#SHADING_MODE
*/
public static final int SHADING_MODE_FAST = 1;
/**
- * <p>Frame rate may be reduced by high quality</p>
+ * <p>Apply high-quality lens shading correction, at the
+ * cost of reduced frame rate.</p>
* @see CaptureRequest#SHADING_MODE
*/
public static final int SHADING_MODE_HIGH_QUALITY = 2;
@@ -1521,20 +1632,28 @@
//
/**
+ * <p>Do not include face detection statistics in capture
+ * results.</p>
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
*/
public static final int STATISTICS_FACE_DETECT_MODE_OFF = 0;
/**
- * <p>Optional Return rectangle and confidence
- * only</p>
+ * <p>Return face rectangle and confidence values only.</p>
+ * <p>In this mode, only android.statistics.faceRectangles and
+ * android.statistics.faceScores outputs are valid.</p>
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
*/
public static final int STATISTICS_FACE_DETECT_MODE_SIMPLE = 1;
/**
- * <p>Optional Return all face
- * metadata</p>
+ * <p>Return all face
+ * metadata.</p>
+ * <p>In this mode,
+ * android.statistics.faceRectangles,
+ * android.statistics.faceScores,
+ * android.statistics.faceIds, and
+ * android.statistics.faceLandmarks outputs are valid.</p>
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
*/
public static final int STATISTICS_FACE_DETECT_MODE_FULL = 2;
@@ -1544,11 +1663,13 @@
//
/**
+ * <p>Do not include a lens shading map in the capture result.</p>
* @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
*/
public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0;
/**
+ * <p>Include a lens shading map in the capture result.</p>
* @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
*/
public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1;
@@ -1572,15 +1693,15 @@
public static final int TONEMAP_MODE_CONTRAST_CURVE = 0;
/**
- * <p>Advanced gamma mapping and color enhancement may be applied.</p>
- * <p>Should not slow down frame rate relative to raw sensor output.</p>
+ * <p>Advanced gamma mapping and color enhancement may be applied, without
+ * reducing frame rate compared to raw sensor output.</p>
* @see CaptureRequest#TONEMAP_MODE
*/
public static final int TONEMAP_MODE_FAST = 1;
/**
- * <p>Advanced gamma mapping and color enhancement may be applied.</p>
- * <p>May slow down frame rate relative to raw sensor output.</p>
+ * <p>High-quality gamma mapping and color enhancement will be applied, at
+ * the cost of reduced frame rate compared to raw sensor output.</p>
* @see CaptureRequest#TONEMAP_MODE
*/
public static final int TONEMAP_MODE_HIGH_QUALITY = 2;
@@ -1590,7 +1711,8 @@
//
/**
- * <p>AE is off or recently reset. When a camera device is opened, it starts in
+ * <p>AE is off or recently reset.</p>
+ * <p>When a camera device is opened, it starts in
* this state. This is a transient state, the camera device may skip reporting
* this state in capture result.</p>
* @see CaptureResult#CONTROL_AE_STATE
@@ -1599,7 +1721,8 @@
/**
* <p>AE doesn't yet have a good set of control values
- * for the current scene. This is a transient state, the camera device may skip
+ * for the current scene.</p>
+ * <p>This is a transient state, the camera device may skip
* reporting this state in capture result.</p>
* @see CaptureResult#CONTROL_AE_STATE
*/
@@ -1628,11 +1751,13 @@
/**
* <p>AE has been asked to do a precapture sequence
- * (through the {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} START),
- * and is currently executing it. Once PRECAPTURE
- * completes, AE will transition to CONVERGED or
- * FLASH_REQUIRED as appropriate. This is a transient state, the
- * camera device may skip reporting this state in capture result.</p>
+ * and is currently executing it.</p>
+ * <p>Precapture can be triggered through setting
+ * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} to START.</p>
+ * <p>Once PRECAPTURE completes, AE will transition to CONVERGED
+ * or FLASH_REQUIRED as appropriate. This is a transient
+ * state, the camera device may skip reporting this state in
+ * capture result.</p>
*
* @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
* @see CaptureResult#CONTROL_AE_STATE
@@ -1644,61 +1769,78 @@
//
/**
- * <p>AF off or has not yet tried to scan/been asked
- * to scan. When a camera device is opened, it starts in
- * this state. This is a transient state, the camera device may
- * skip reporting this state in capture result.</p>
+ * <p>AF is off or has not yet tried to scan/been asked
+ * to scan.</p>
+ * <p>When a camera device is opened, it starts in this
+ * state. This is a transient state, the camera device may
+ * skip reporting this state in capture
+ * result.</p>
* @see CaptureResult#CONTROL_AF_STATE
*/
public static final int CONTROL_AF_STATE_INACTIVE = 0;
/**
- * <p>if CONTINUOUS_* modes are supported. AF is
- * currently doing an AF scan initiated by a continuous
- * autofocus mode. This is a transient state, the camera device may
- * skip reporting this state in capture result.</p>
+ * <p>AF is currently performing an AF scan initiated the
+ * camera device in a continuous autofocus mode.</p>
+ * <p>Only used by CONTINUOUS_* AF modes. This is a transient
+ * state, the camera device may skip reporting this state in
+ * capture result.</p>
* @see CaptureResult#CONTROL_AF_STATE
*/
public static final int CONTROL_AF_STATE_PASSIVE_SCAN = 1;
/**
- * <p>if CONTINUOUS_* modes are supported. AF currently
- * believes it is in focus, but may restart scanning at
- * any time. This is a transient state, the camera device may skip
- * reporting this state in capture result.</p>
+ * <p>AF currently believes it is in focus, but may
+ * restart scanning at any time.</p>
+ * <p>Only used by CONTINUOUS_* AF modes. This is a transient
+ * state, the camera device may skip reporting this state in
+ * capture result.</p>
* @see CaptureResult#CONTROL_AF_STATE
*/
public static final int CONTROL_AF_STATE_PASSIVE_FOCUSED = 2;
/**
- * <p>if AUTO or MACRO modes are supported. AF is doing
- * an AF scan because it was triggered by AF trigger. This is a
- * transient state, the camera device may skip reporting
- * this state in capture result.</p>
+ * <p>AF is performing an AF scan because it was
+ * triggered by AF trigger.</p>
+ * <p>Only used by AUTO or MACRO AF modes. This is a transient
+ * state, the camera device may skip reporting this state in
+ * capture result.</p>
* @see CaptureResult#CONTROL_AF_STATE
*/
public static final int CONTROL_AF_STATE_ACTIVE_SCAN = 3;
/**
- * <p>if any AF mode besides OFF is supported. AF
- * believes it is focused correctly and is
- * locked.</p>
+ * <p>AF believes it is focused correctly and has locked
+ * focus.</p>
+ * <p>This state is reached only after an explicit START AF trigger has been
+ * sent ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}), when good focus has been obtained.</p>
+ * <p>The lens will remain stationary until the AF mode ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) is changed or
+ * a new AF trigger is sent to the camera device ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}).</p>
+ *
+ * @see CaptureRequest#CONTROL_AF_MODE
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
* @see CaptureResult#CONTROL_AF_STATE
*/
public static final int CONTROL_AF_STATE_FOCUSED_LOCKED = 4;
/**
- * <p>if any AF mode besides OFF is supported. AF has
- * failed to focus successfully and is
- * locked.</p>
+ * <p>AF has failed to focus successfully and has locked
+ * focus.</p>
+ * <p>This state is reached only after an explicit START AF trigger has been
+ * sent ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}), when good focus cannot be obtained.</p>
+ * <p>The lens will remain stationary until the AF mode ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) is changed or
+ * a new AF trigger is sent to the camera device ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}).</p>
+ *
+ * @see CaptureRequest#CONTROL_AF_MODE
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
* @see CaptureResult#CONTROL_AF_STATE
*/
public static final int CONTROL_AF_STATE_NOT_FOCUSED_LOCKED = 5;
/**
- * <p>if CONTINUOUS_* modes are supported. AF finished a
- * passive scan without finding focus, and may restart
- * scanning at any time. This is a transient state, the camera
+ * <p>AF finished a passive scan without finding focus,
+ * and may restart scanning at any time.</p>
+ * <p>Only used by CONTINUOUS_* AF modes. This is a transient state, the camera
* device may skip reporting this state in capture result.</p>
* @see CaptureResult#CONTROL_AF_STATE
*/
@@ -1709,16 +1851,19 @@
//
/**
- * <p>AWB is not in auto mode. When a camera device is opened, it
- * starts in this state. This is a transient state, the camera device may
- * skip reporting this state in capture result.</p>
+ * <p>AWB is not in auto mode, or has not yet started metering.</p>
+ * <p>When a camera device is opened, it starts in this
+ * state. This is a transient state, the camera device may
+ * skip reporting this state in capture
+ * result.</p>
* @see CaptureResult#CONTROL_AWB_STATE
*/
public static final int CONTROL_AWB_STATE_INACTIVE = 0;
/**
* <p>AWB doesn't yet have a good set of control
- * values for the current scene. This is a transient state, the camera device
+ * values for the current scene.</p>
+ * <p>This is a transient state, the camera device
* may skip reporting this state in capture result.</p>
* @see CaptureResult#CONTROL_AWB_STATE
*/
@@ -1766,8 +1911,9 @@
public static final int FLASH_STATE_FIRED = 3;
/**
- * <p>Flash partially illuminated this frame. This is usually due to the next
- * or previous frame having the flash fire, and the flash spilling into this capture
+ * <p>Flash partially illuminated this frame.</p>
+ * <p>This is usually due to the next or previous frame having
+ * the flash fire, and the flash spilling into this capture
* due to hardware limitations.</p>
* @see CaptureResult#FLASH_STATE
*/
@@ -1790,8 +1936,10 @@
public static final int LENS_STATE_STATIONARY = 0;
/**
- * <p>Any of the lens parameters ({@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}, {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance},
- * {@link CaptureRequest#LENS_FILTER_DENSITY android.lens.filterDensity} or {@link CaptureRequest#LENS_APERTURE android.lens.aperture}) is changing.</p>
+ * <p>One or several of the lens parameters
+ * ({@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}, {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance},
+ * {@link CaptureRequest#LENS_FILTER_DENSITY android.lens.filterDensity} or {@link CaptureRequest#LENS_APERTURE android.lens.aperture}) is
+ * currently changing.</p>
*
* @see CaptureRequest#LENS_APERTURE
* @see CaptureRequest#LENS_FILTER_DENSITY
@@ -1806,16 +1954,22 @@
//
/**
+ * <p>The camera device does not detect any flickering illumination
+ * in the current scene.</p>
* @see CaptureResult#STATISTICS_SCENE_FLICKER
*/
public static final int STATISTICS_SCENE_FLICKER_NONE = 0;
/**
+ * <p>The camera device detects illumination flickering at 50Hz
+ * in the current scene.</p>
* @see CaptureResult#STATISTICS_SCENE_FLICKER
*/
public static final int STATISTICS_SCENE_FLICKER_50HZ = 1;
/**
+ * <p>The camera device detects illumination flickering at 60Hz
+ * in the current scene.</p>
* @see CaptureResult#STATISTICS_SCENE_FLICKER
*/
public static final int STATISTICS_SCENE_FLICKER_60HZ = 2;
@@ -1825,8 +1979,8 @@
//
/**
- * <p>The current result is not yet fully synchronized to any request.
- * Synchronization is in progress, and reading metadata from this
+ * <p>The current result is not yet fully synchronized to any request.</p>
+ * <p>Synchronization is in progress, and reading metadata from this
* result may include a mix of data that have taken effect since the
* last synchronization time.</p>
* <p>In some future result, within {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} frames,
@@ -1841,10 +1995,10 @@
public static final int SYNC_FRAME_NUMBER_CONVERGING = -1;
/**
- * <p>The current result's synchronization status is unknown. The
- * result may have already converged, or it may be in progress.
- * Reading from this result may include some mix of settings from
- * past requests.</p>
+ * <p>The current result's synchronization status is unknown.</p>
+ * <p>The result may have already converged, or it may be in
+ * progress. Reading from this result may include some mix
+ * of settings from past requests.</p>
* <p>After a settings change, the new settings will eventually all
* take effect for the output buffers and results. However, this
* value will not change when that happens. Altering settings
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index d4dfdd5..bf7bd37 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -43,14 +43,14 @@
* <p>CaptureRequests can be created by using a {@link Builder} instance,
* obtained by calling {@link CameraDevice#createCaptureRequest}</p>
*
- * <p>CaptureRequests are given to {@link CameraDevice#capture} or
- * {@link CameraDevice#setRepeatingRequest} to capture images from a camera.</p>
+ * <p>CaptureRequests are given to {@link CameraCaptureSession#capture} or
+ * {@link CameraCaptureSession#setRepeatingRequest} to capture images from a camera.</p>
*
* <p>Each request can specify a different subset of target Surfaces for the
* camera to send the captured data to. All the surfaces used in a request must
* be part of the surface list given to the last call to
- * {@link CameraDevice#configureOutputs}, when the request is submitted to the
- * camera device.</p>
+ * {@link CameraDevice#createCaptureSession}, when the request is submitted to the
+ * session.</p>
*
* <p>For example, a request meant for repeating preview might only include the
* Surface for the preview SurfaceView or SurfaceTexture, while a
@@ -357,11 +357,11 @@
* request fields to one of the templates defined in {@link CameraDevice}.
*
* @see CameraDevice#createCaptureRequest
- * @see #TEMPLATE_PREVIEW
- * @see #TEMPLATE_RECORD
- * @see #TEMPLATE_STILL_CAPTURE
- * @see #TEMPLATE_VIDEO_SNAPSHOT
- * @see #TEMPLATE_MANUAL
+ * @see CameraDevice#TEMPLATE_PREVIEW
+ * @see CameraDevice#TEMPLATE_RECORD
+ * @see CameraDevice#TEMPLATE_STILL_CAPTURE
+ * @see CameraDevice#TEMPLATE_VIDEO_SNAPSHOT
+ * @see CameraDevice#TEMPLATE_MANUAL
*/
public final static class Builder {
@@ -381,7 +381,7 @@
* <p>Add a surface to the list of targets for this request</p>
*
* <p>The Surface added must be one of the surfaces included in the most
- * recent call to {@link CameraDevice#configureOutputs}, when the
+ * recent call to {@link CameraDevice#createCaptureSession}, when the
* request is given to the camera device.</p>
*
* <p>Adding a target more than once has no effect.</p>
@@ -473,7 +473,7 @@
/**
* <p>The mode control selects how the image data is converted from the
* sensor's native color into linear sRGB color.</p>
- * <p>When auto-white balance is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this
+ * <p>When auto-white balance (AWB) is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this
* control is overridden by the AWB routine. When AWB is disabled, the
* application controls how the color mapping is performed.</p>
* <p>We define the expected processing pipeline below. For consistency
@@ -524,7 +524,7 @@
/**
* <p>A color transform matrix to use to transform
- * from sensor RGB color space to output linear sRGB color space</p>
+ * from sensor RGB color space to output linear sRGB color space.</p>
* <p>This matrix is either set by the camera device when the request
* {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not TRANSFORM_MATRIX, or
* directly by the application in the request when the
@@ -600,13 +600,17 @@
new Key<Integer>("android.control.aeAntibandingMode", int.class);
/**
- * <p>Adjustment to AE target image
- * brightness</p>
- * <p>For example, if EV step is 0.333, '6' will mean an
- * exposure compensation of +2 EV; -3 will mean an exposure
- * compensation of -1 EV. Note that this control will only be effective
- * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when
- * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p>
+ * <p>Adjustment to auto-exposure (AE) target image
+ * brightness.</p>
+ * <p>The adjustment is measured as a count of steps, with the
+ * step size defined by {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} and the
+ * allowed range by {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE android.control.aeCompensationRange}.</p>
+ * <p>For example, if the exposure value (EV) step is 0.333, '6'
+ * will mean an exposure compensation of +2 EV; -3 will mean an
+ * exposure compensation of -1 EV. One EV represents a doubling
+ * of image brightness. Note that this control will only be
+ * effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control
+ * will take effect even when {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p>
* <p>In the event of exposure compensation value being changed, camera device
* may take several frames to reach the newly requested exposure target.
* During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING
@@ -614,6 +618,8 @@
* change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or
* FLASH_REQUIRED (if the scene is too dark for still capture).</p>
*
+ * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE
+ * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP
* @see CaptureRequest#CONTROL_AE_LOCK
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureResult#CONTROL_AE_STATE
@@ -622,7 +628,7 @@
new Key<Integer>("android.control.aeExposureCompensation", int.class);
/**
- * <p>Whether AE is currently locked to its latest
+ * <p>Whether auto-exposure (AE) is currently locked to its latest
* calculated values.</p>
* <p>Note that even when AE is locked, the flash may be
* fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH /
@@ -711,9 +717,9 @@
/**
* <p>Range over which fps can be adjusted to
- * maintain exposure</p>
- * <p>Only constrains AE algorithm, not manual control
- * of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p>
+ * maintain exposure.</p>
+ * <p>Only constrains auto-exposure (AE) algorithm, not
+ * manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p>
*
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
*/
@@ -727,9 +733,18 @@
* included at all in the request settings. When included and
* set to START, the camera device will trigger the autoexposure
* precapture metering sequence.</p>
- * <p>The effect of AE precapture trigger depends on the current
- * AE mode and state; see {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture
- * state transition details.</p>
+ * <p>The precapture sequence should triggered before starting a
+ * high-quality still capture for final metering decisions to
+ * be made, and for firing pre-capture flash pulses to estimate
+ * scene brightness and required final capture flash power, when
+ * the flash is enabled.</p>
+ * <p>Normally, this entry should be set to START for only a
+ * single request, and the application should wait until the
+ * sequence completes before starting a new one.</p>
+ * <p>The exact effect of auto-exposure (AE) precapture trigger
+ * depends on the current AE mode and state; see
+ * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition
+ * details.</p>
*
* @see CaptureResult#CONTROL_AE_STATE
* @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
@@ -739,8 +754,8 @@
new Key<Integer>("android.control.aePrecaptureTrigger", int.class);
/**
- * <p>Whether AF is currently enabled, and what
- * mode it is set to</p>
+ * <p>Whether auto-focus (AF) is currently enabled, and what
+ * mode it is set to.</p>
* <p>Only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} = AUTO and the lens is not fixed focus
* (i.e. <code>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} > 0</code>).</p>
* <p>If the lens is controlled by the camera device auto-focus algorithm,
@@ -793,7 +808,11 @@
* autofocus algorithm. If autofocus is disabled, this trigger has no effect.</p>
* <p>When set to CANCEL, the camera device will cancel any active trigger,
* and return to its initial AF state.</p>
- * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what that means for each AF mode.</p>
+ * <p>Generally, applications should set this entry to START or CANCEL for only a
+ * single capture, and then return it to IDLE (or not set at all). Specifying
+ * START for multiple captures in a row means restarting the AF operation over
+ * and over again.</p>
+ * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p>
*
* @see CaptureResult#CONTROL_AF_STATE
* @see #CONTROL_AF_TRIGGER_IDLE
@@ -804,31 +823,37 @@
new Key<Integer>("android.control.afTrigger", int.class);
/**
- * <p>Whether AWB is currently locked to its
+ * <p>Whether auto-white balance (AWB) is currently locked to its
* latest calculated values.</p>
- * <p>Note that AWB lock is only meaningful for AUTO
- * mode; in other modes, AWB is already fixed to a specific
- * setting.</p>
+ * <p>Note that AWB lock is only meaningful when
+ * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} is in the AUTO mode; in other modes,
+ * AWB is already fixed to a specific setting.</p>
+ *
+ * @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final Key<Boolean> CONTROL_AWB_LOCK =
new Key<Boolean>("android.control.awbLock", boolean.class);
/**
- * <p>Whether AWB is currently setting the color
+ * <p>Whether auto-white balance (AWB) is currently setting the color
* transform fields, and what its illumination target
* is.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is AUTO.</p>
- * <p>When set to the ON mode, the camera device's auto white balance
+ * <p>When set to the ON mode, the camera device's auto-white balance
* routine is enabled, overriding the application's selected
* {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
* {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p>
- * <p>When set to the OFF mode, the camera device's auto white balance
+ * <p>When set to the OFF mode, the camera device's auto-white balance
* routine is disabled. The application manually controls the white
* balance by {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}
* and {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p>
- * <p>When set to any other modes, the camera device's auto white balance
- * routine is disabled. The camera device uses each particular illumination
- * target for white balance adjustment.</p>
+ * <p>When set to any other modes, the camera device's auto-white
+ * balance routine is disabled. The camera device uses each
+ * particular illumination target for white balance
+ * adjustment. The application's values for
+ * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform},
+ * {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
+ * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} are ignored.</p>
*
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @see CaptureRequest#COLOR_CORRECTION_MODE
@@ -879,8 +904,8 @@
* strategy.</p>
* <p>This control (except for MANUAL) is only effective if
* <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
- * <p>ZERO_SHUTTER_LAG must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
- * contains ZSL. MANUAL must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+ * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+ * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
* contains MANUAL_SENSOR.</p>
*
* @see CaptureRequest#CONTROL_MODE
@@ -955,7 +980,9 @@
* <p>This is the mode that that is active when
* <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code>. Aside from FACE_PRIORITY,
* these modes will disable {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode},
- * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.</p>
+ * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.
+ * The scene modes available for a given camera device are listed in
+ * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}.</p>
* <p>The interpretation and implementation of these scene modes is left
* to the implementor of the camera device. Their behavior will not be
* consistent across all devices, and any given device may only implement
@@ -963,6 +990,7 @@
*
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_AF_MODE
+ * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES
* @see CaptureRequest#CONTROL_AWB_MODE
* @see CaptureRequest#CONTROL_MODE
* @see #CONTROL_SCENE_MODE_DISABLED
@@ -988,7 +1016,9 @@
/**
* <p>Whether video stabilization is
- * active</p>
+ * active.</p>
+ * <p>Video stabilization automatically translates and scales images from the camera
+ * in order to stabilize motion between consecutive frames.</p>
* <p>If enabled, video stabilization can modify the
* {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream
* stabilized</p>
@@ -1030,7 +1060,7 @@
* <p>When set to OFF, the camera device will not fire flash for this capture.</p>
* <p>When set to SINGLE, the camera device will fire flash regardless of the camera
* device's auto-exposure routine's result. When used in still capture case, this
- * control should be used along with AE precapture metering sequence
+ * control should be used along with auto-exposure (AE) precapture metering sequence
* ({@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}), otherwise, the image may be incorrectly exposed.</p>
* <p>When set to TORCH, the flash will be on continuously. This mode can be used
* for use cases such as preview, auto-focus assist, still capture, or video recording.</p>
@@ -1102,21 +1132,21 @@
/**
* <p>Compression quality of the final JPEG
- * image</p>
- * <p>85-95 is typical usage range</p>
+ * image.</p>
+ * <p>85-95 is typical usage range.</p>
*/
public static final Key<Byte> JPEG_QUALITY =
new Key<Byte>("android.jpeg.quality", byte.class);
/**
* <p>Compression quality of JPEG
- * thumbnail</p>
+ * thumbnail.</p>
*/
public static final Key<Byte> JPEG_THUMBNAIL_QUALITY =
new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
/**
- * <p>Resolution of embedded JPEG thumbnail</p>
+ * <p>Resolution of embedded JPEG thumbnail.</p>
* <p>When set to (0, 0) value, the JPEG EXIF will not contain thumbnail,
* but the captured JPEG will still be a valid image.</p>
* <p>When a jpeg image capture is issued, the thumbnail size selected should have
@@ -1204,7 +1234,7 @@
/**
* <p>Distance to plane of sharpest focus,
- * measured from frontmost surface of the lens</p>
+ * measured from frontmost surface of the lens.</p>
* <p>0 means infinity focus. Used value will be clamped
* to [0, {@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance}].</p>
* <p>Like {@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}, this setting won't be applied
@@ -1222,12 +1252,18 @@
/**
* <p>Sets whether the camera device uses optical image stabilization (OIS)
* when capturing images.</p>
- * <p>OIS is used to compensate for motion blur due to small movements of
- * the camera during capture. Unlike digital image stabilization, OIS makes
- * use of mechanical elements to stabilize the camera sensor, and thus
- * allows for longer exposure times before camera shake becomes
- * apparent.</p>
- * <p>This is not expected to be supported on most devices.</p>
+ * <p>OIS is used to compensate for motion blur due to small
+ * movements of the camera during capture. Unlike digital image
+ * stabilization ({@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode}), OIS
+ * makes use of mechanical elements to stabilize the camera
+ * sensor, and thus allows for longer exposure times before
+ * camera shake becomes apparent.</p>
+ * <p>Not all devices will support OIS; see
+ * {@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization} for
+ * available controls.</p>
+ *
+ * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
+ * @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION
* @see #LENS_OPTICAL_STABILIZATION_MODE_OFF
* @see #LENS_OPTICAL_STABILIZATION_MODE_ON
*/
@@ -1235,16 +1271,15 @@
new Key<Integer>("android.lens.opticalStabilizationMode", int.class);
/**
- * <p>Mode of operation for the noise reduction
- * algorithm</p>
+ * <p>Mode of operation for the noise reduction algorithm.</p>
* <p>Noise filtering control. OFF means no noise reduction
* will be applied by the camera device.</p>
- * <p>This must be set to a valid mode in
+ * <p>This must be set to a valid mode from
* {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}.</p>
* <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering
* will be applied. HIGH_QUALITY mode indicates that the camera device
* will use the highest-quality noise filtering algorithms,
- * even if it slows down capture rate. FAST means the camera device should not
+ * even if it slows down capture rate. FAST means the camera device will not
* slow down capture rate when applying noise filtering.</p>
*
* @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
@@ -1265,40 +1300,39 @@
new Key<Integer>("android.request.id", int.class);
/**
- * <p>(x, y, width, height).</p>
- * <p>A rectangle with the top-level corner of (x,y) and size
- * (width, height). The region of the sensor that is used for
- * output. Each stream must use this rectangle to produce its
- * output, cropping to a smaller region if necessary to
- * maintain the stream's aspect ratio.</p>
- * <p>HAL2.x uses only (x, y, width)</p>
- * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
- * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage,
- * it is not croppable. The crop region will be ignored by raw streams.</p>
+ * <p>The region of the sensor to read out for this capture.</p>
+ * <p>The crop region coordinate system is based off
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the
+ * top-left corner of the sensor active array.</p>
+ * <p>Output streams use this rectangle to produce their output,
+ * cropping to a smaller region if necessary to maintain the
+ * stream's aspect ratio, then scaling the sensor input to
+ * match the output's configured resolution.</p>
+ * <p>The crop region is applied after the RAW to other color
+ * space (e.g. YUV) conversion. Since raw streams
+ * (e.g. RAW16) don't have the conversion stage, they are not
+ * croppable. The crop region will be ignored by raw streams.</p>
* <p>For non-raw streams, any additional per-stream cropping will
* be done to maximize the final pixel area of the stream.</p>
* <p>For example, if the crop region is set to a 4:3 aspect
- * ratio, then 4:3 streams should use the exact crop
- * region. 16:9 streams should further crop vertically
+ * ratio, then 4:3 streams will use the exact crop
+ * region. 16:9 streams will further crop vertically
* (letterbox).</p>
* <p>Conversely, if the crop region is set to a 16:9, then 4:3
- * outputs should crop horizontally (pillarbox), and 16:9
- * streams should match exactly. These additional crops must
+ * outputs will crop horizontally (pillarbox), and 16:9
+ * streams will match exactly. These additional crops will
* be centered within the crop region.</p>
- * <p>The output streams must maintain square pixels at all
- * times, no matter what the relative aspect ratios of the
- * crop region and the stream are. Negative values for
- * corner are allowed for raw output if full pixel array is
- * larger than active pixel array. Width and height may be
- * rounded to nearest larger supportable width, especially
- * for raw output, where only a few fixed scales may be
- * possible. The width and height of the crop region cannot
- * be set to be smaller than floor( activeArraySize.width /
- * {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} ) and floor(
- * activeArraySize.height /
- * {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom}), respectively.</p>
+ * <p>The width and height of the crop region cannot
+ * be set to be smaller than
+ * <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
+ * <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p>
+ * <p>The camera device may adjust the crop region to account
+ * for rounding and other hardware requirements; the final
+ * crop region used will be included in the output capture
+ * result.</p>
*
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
@@ -1391,12 +1425,20 @@
new Key<Long>("android.sensor.frameDuration", long.class);
/**
- * <p>Gain applied to image data. Must be
- * implemented through analog gain only if set to values
- * below 'maximum analog sensitivity'.</p>
- * <p>If the sensor can't apply this exact gain, it should lessen the
- * gain to the nearest possible value (rather than gain more).</p>
- * <p>ISO 12232:2006 REI method</p>
+ * <p>The amount of gain applied to sensor data
+ * before processing.</p>
+ * <p>The sensitivity is the standard ISO sensitivity value,
+ * as defined in ISO 12232:2006.</p>
+ * <p>The sensitivity must be within {@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}, and
+ * if if it less than {@link CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY android.sensor.maxAnalogSensitivity}, the camera device
+ * is guaranteed to use only analog amplification for applying the gain.</p>
+ * <p>If the camera device cannot apply the exact sensitivity
+ * requested, it will reduce the gain to the nearest supported
+ * value. The final sensitivity used will be available in the
+ * output capture result.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
+ * @see CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY
*/
public static final Key<Integer> SENSOR_SENSITIVITY =
new Key<Integer>("android.sensor.sensitivity", int.class);
@@ -1421,7 +1463,7 @@
* <p>When enabled, the sensor sends a test pattern instead of
* doing a real exposure from the camera.</p>
* <p>When a test pattern is enabled, all manual sensor controls specified
- * by android.sensor.* should be ignored. All other controls should
+ * by android.sensor.* will be ignored. All other controls should
* work as normal.</p>
* <p>For example, if manual flash is enabled, flash firing should still
* occur (and that the test pattern remain unmodified, since the flash
@@ -1459,7 +1501,7 @@
* lens shading map data in android.statistics.lensShadingMap, with size specified
* by android.lens.info.shadingMapSize; the returned shading map data will be the one
* applied by the camera device for this capture request.</p>
- * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
+ * <p>The shading map data may depend on the auto-exposure (AE) and AWB statistics, therefore the reliability
* of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
* AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF),
* to get best results, it is recommended that the applications wait for the AE and AWB to
@@ -1476,8 +1518,8 @@
new Key<Integer>("android.shading.mode", int.class);
/**
- * <p>State of the face detector
- * unit</p>
+ * <p>Control for the face detector
+ * unit.</p>
* <p>Whether face detection is enabled, and whether it
* should output just the basic fields or the full set of
* fields. Value must be one of the
@@ -1494,7 +1536,7 @@
/**
* <p>Operating mode for hotpixel map generation.</p>
* <p>If set to ON, a hotpixel map is returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.
- * If set to OFF, no hotpixel map should be returned.</p>
+ * If set to OFF, no hotpixel map will be returned.</p>
* <p>This must be set to a valid mode from {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES android.statistics.info.availableHotPixelMapModes}.</p>
*
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
@@ -1507,7 +1549,7 @@
* <p>Whether the camera device will output the lens
* shading map in output result metadata.</p>
* <p>When set to ON,
- * android.statistics.lensShadingMap must be provided in
+ * android.statistics.lensShadingMap will be provided in
* the output result metadata.</p>
* @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
* @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 7d07c92..3d17ed3 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -319,7 +319,7 @@
/**
* <p>The mode control selects how the image data is converted from the
* sensor's native color into linear sRGB color.</p>
- * <p>When auto-white balance is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this
+ * <p>When auto-white balance (AWB) is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this
* control is overridden by the AWB routine. When AWB is disabled, the
* application controls how the color mapping is performed.</p>
* <p>We define the expected processing pipeline below. For consistency
@@ -370,7 +370,7 @@
/**
* <p>A color transform matrix to use to transform
- * from sensor RGB color space to output linear sRGB color space</p>
+ * from sensor RGB color space to output linear sRGB color space.</p>
* <p>This matrix is either set by the camera device when the request
* {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} is not TRANSFORM_MATRIX, or
* directly by the application in the request when the
@@ -446,13 +446,17 @@
new Key<Integer>("android.control.aeAntibandingMode", int.class);
/**
- * <p>Adjustment to AE target image
- * brightness</p>
- * <p>For example, if EV step is 0.333, '6' will mean an
- * exposure compensation of +2 EV; -3 will mean an exposure
- * compensation of -1 EV. Note that this control will only be effective
- * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when
- * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p>
+ * <p>Adjustment to auto-exposure (AE) target image
+ * brightness.</p>
+ * <p>The adjustment is measured as a count of steps, with the
+ * step size defined by {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} and the
+ * allowed range by {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE android.control.aeCompensationRange}.</p>
+ * <p>For example, if the exposure value (EV) step is 0.333, '6'
+ * will mean an exposure compensation of +2 EV; -3 will mean an
+ * exposure compensation of -1 EV. One EV represents a doubling
+ * of image brightness. Note that this control will only be
+ * effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control
+ * will take effect even when {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p>
* <p>In the event of exposure compensation value being changed, camera device
* may take several frames to reach the newly requested exposure target.
* During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING
@@ -460,6 +464,8 @@
* change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or
* FLASH_REQUIRED (if the scene is too dark for still capture).</p>
*
+ * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE
+ * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP
* @see CaptureRequest#CONTROL_AE_LOCK
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureResult#CONTROL_AE_STATE
@@ -468,7 +474,7 @@
new Key<Integer>("android.control.aeExposureCompensation", int.class);
/**
- * <p>Whether AE is currently locked to its latest
+ * <p>Whether auto-exposure (AE) is currently locked to its latest
* calculated values.</p>
* <p>Note that even when AE is locked, the flash may be
* fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH /
@@ -557,9 +563,9 @@
/**
* <p>Range over which fps can be adjusted to
- * maintain exposure</p>
- * <p>Only constrains AE algorithm, not manual control
- * of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p>
+ * maintain exposure.</p>
+ * <p>Only constrains auto-exposure (AE) algorithm, not
+ * manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p>
*
* @see CaptureRequest#SENSOR_EXPOSURE_TIME
*/
@@ -573,9 +579,18 @@
* included at all in the request settings. When included and
* set to START, the camera device will trigger the autoexposure
* precapture metering sequence.</p>
- * <p>The effect of AE precapture trigger depends on the current
- * AE mode and state; see {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture
- * state transition details.</p>
+ * <p>The precapture sequence should triggered before starting a
+ * high-quality still capture for final metering decisions to
+ * be made, and for firing pre-capture flash pulses to estimate
+ * scene brightness and required final capture flash power, when
+ * the flash is enabled.</p>
+ * <p>Normally, this entry should be set to START for only a
+ * single request, and the application should wait until the
+ * sequence completes before starting a new one.</p>
+ * <p>The exact effect of auto-exposure (AE) precapture trigger
+ * depends on the current AE mode and state; see
+ * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition
+ * details.</p>
*
* @see CaptureResult#CONTROL_AE_STATE
* @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
@@ -585,7 +600,7 @@
new Key<Integer>("android.control.aePrecaptureTrigger", int.class);
/**
- * <p>Current state of AE algorithm</p>
+ * <p>Current state of the auto-exposure (AE) algorithm.</p>
* <p>Switching between or enabling AE modes ({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}) always
* resets the AE state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode},
* or {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} if <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code> resets all
@@ -783,8 +798,8 @@
new Key<Integer>("android.control.aeState", int.class);
/**
- * <p>Whether AF is currently enabled, and what
- * mode it is set to</p>
+ * <p>Whether auto-focus (AF) is currently enabled, and what
+ * mode it is set to.</p>
* <p>Only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} = AUTO and the lens is not fixed focus
* (i.e. <code>{@link CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE android.lens.info.minimumFocusDistance} > 0</code>).</p>
* <p>If the lens is controlled by the camera device auto-focus algorithm,
@@ -837,7 +852,11 @@
* autofocus algorithm. If autofocus is disabled, this trigger has no effect.</p>
* <p>When set to CANCEL, the camera device will cancel any active trigger,
* and return to its initial AF state.</p>
- * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what that means for each AF mode.</p>
+ * <p>Generally, applications should set this entry to START or CANCEL for only a
+ * single capture, and then return it to IDLE (or not set at all). Specifying
+ * START for multiple captures in a row means restarting the AF operation over
+ * and over again.</p>
+ * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p>
*
* @see CaptureResult#CONTROL_AF_STATE
* @see #CONTROL_AF_TRIGGER_IDLE
@@ -848,7 +867,7 @@
new Key<Integer>("android.control.afTrigger", int.class);
/**
- * <p>Current state of AF algorithm.</p>
+ * <p>Current state of auto-focus (AF) algorithm.</p>
* <p>Switching between or enabling AF modes ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) always
* resets the AF state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode},
* or {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} if <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code> resets all
@@ -1027,13 +1046,13 @@
* <td align="center">PASSIVE_SCAN</td>
* <td align="center">AF_TRIGGER</td>
* <td align="center">FOCUSED_LOCKED</td>
- * <td align="center">Immediate trans. If focus is good, Lens now locked</td>
+ * <td align="center">Immediate transition, if focus is good. Lens now locked</td>
* </tr>
* <tr>
* <td align="center">PASSIVE_SCAN</td>
* <td align="center">AF_TRIGGER</td>
* <td align="center">NOT_FOCUSED_LOCKED</td>
- * <td align="center">Immediate trans. if focus is bad, Lens now locked</td>
+ * <td align="center">Immediate transition, if focus is bad. Lens now locked</td>
* </tr>
* <tr>
* <td align="center">PASSIVE_SCAN</td>
@@ -1057,13 +1076,13 @@
* <td align="center">PASSIVE_FOCUSED</td>
* <td align="center">AF_TRIGGER</td>
* <td align="center">FOCUSED_LOCKED</td>
- * <td align="center">Immediate trans. Lens now locked</td>
+ * <td align="center">Immediate transition, lens now locked</td>
* </tr>
* <tr>
* <td align="center">PASSIVE_UNFOCUSED</td>
* <td align="center">AF_TRIGGER</td>
* <td align="center">NOT_FOCUSED_LOCKED</td>
- * <td align="center">Immediate trans. Lens now locked</td>
+ * <td align="center">Immediate transition, lens now locked</td>
* </tr>
* <tr>
* <td align="center">FOCUSED_LOCKED</td>
@@ -1130,13 +1149,13 @@
* <td align="center">PASSIVE_SCAN</td>
* <td align="center">AF_TRIGGER</td>
* <td align="center">FOCUSED_LOCKED</td>
- * <td align="center">Eventual trans. once focus good, Lens now locked</td>
+ * <td align="center">Eventual transition once the focus is good. Lens now locked</td>
* </tr>
* <tr>
* <td align="center">PASSIVE_SCAN</td>
* <td align="center">AF_TRIGGER</td>
* <td align="center">NOT_FOCUSED_LOCKED</td>
- * <td align="center">Eventual trans. if cannot focus, Lens now locked</td>
+ * <td align="center">Eventual transition if cannot find focus. Lens now locked</td>
* </tr>
* <tr>
* <td align="center">PASSIVE_SCAN</td>
@@ -1245,31 +1264,37 @@
new Key<Integer>("android.control.afState", int.class);
/**
- * <p>Whether AWB is currently locked to its
+ * <p>Whether auto-white balance (AWB) is currently locked to its
* latest calculated values.</p>
- * <p>Note that AWB lock is only meaningful for AUTO
- * mode; in other modes, AWB is already fixed to a specific
- * setting.</p>
+ * <p>Note that AWB lock is only meaningful when
+ * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} is in the AUTO mode; in other modes,
+ * AWB is already fixed to a specific setting.</p>
+ *
+ * @see CaptureRequest#CONTROL_AWB_MODE
*/
public static final Key<Boolean> CONTROL_AWB_LOCK =
new Key<Boolean>("android.control.awbLock", boolean.class);
/**
- * <p>Whether AWB is currently setting the color
+ * <p>Whether auto-white balance (AWB) is currently setting the color
* transform fields, and what its illumination target
* is.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is AUTO.</p>
- * <p>When set to the ON mode, the camera device's auto white balance
+ * <p>When set to the ON mode, the camera device's auto-white balance
* routine is enabled, overriding the application's selected
* {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
* {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p>
- * <p>When set to the OFF mode, the camera device's auto white balance
+ * <p>When set to the OFF mode, the camera device's auto-white balance
* routine is disabled. The application manually controls the white
* balance by {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}
* and {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p>
- * <p>When set to any other modes, the camera device's auto white balance
- * routine is disabled. The camera device uses each particular illumination
- * target for white balance adjustment.</p>
+ * <p>When set to any other modes, the camera device's auto-white
+ * balance routine is disabled. The camera device uses each
+ * particular illumination target for white balance
+ * adjustment. The application's values for
+ * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform},
+ * {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
+ * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} are ignored.</p>
*
* @see CaptureRequest#COLOR_CORRECTION_GAINS
* @see CaptureRequest#COLOR_CORRECTION_MODE
@@ -1320,8 +1345,8 @@
* strategy.</p>
* <p>This control (except for MANUAL) is only effective if
* <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
- * <p>ZERO_SHUTTER_LAG must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
- * contains ZSL. MANUAL must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+ * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+ * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
* contains MANUAL_SENSOR.</p>
*
* @see CaptureRequest#CONTROL_MODE
@@ -1338,7 +1363,7 @@
new Key<Integer>("android.control.captureIntent", int.class);
/**
- * <p>Current state of AWB algorithm</p>
+ * <p>Current state of auto-white balance (AWB) algorithm.</p>
* <p>Switching between or enabling AWB modes ({@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}) always
* resets the AWB state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode},
* or {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} if <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code> resets all
@@ -1526,7 +1551,9 @@
* <p>This is the mode that that is active when
* <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code>. Aside from FACE_PRIORITY,
* these modes will disable {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode},
- * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.</p>
+ * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.
+ * The scene modes available for a given camera device are listed in
+ * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}.</p>
* <p>The interpretation and implementation of these scene modes is left
* to the implementor of the camera device. Their behavior will not be
* consistent across all devices, and any given device may only implement
@@ -1534,6 +1561,7 @@
*
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_AF_MODE
+ * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES
* @see CaptureRequest#CONTROL_AWB_MODE
* @see CaptureRequest#CONTROL_MODE
* @see #CONTROL_SCENE_MODE_DISABLED
@@ -1559,7 +1587,9 @@
/**
* <p>Whether video stabilization is
- * active</p>
+ * active.</p>
+ * <p>Video stabilization automatically translates and scales images from the camera
+ * in order to stabilize motion between consecutive frames.</p>
* <p>If enabled, video stabilization can modify the
* {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream
* stabilized</p>
@@ -1601,7 +1631,7 @@
* <p>When set to OFF, the camera device will not fire flash for this capture.</p>
* <p>When set to SINGLE, the camera device will fire flash regardless of the camera
* device's auto-exposure routine's result. When used in still capture case, this
- * control should be used along with AE precapture metering sequence
+ * control should be used along with auto-exposure (AE) precapture metering sequence
* ({@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}), otherwise, the image may be incorrectly exposed.</p>
* <p>When set to TORCH, the flash will be on continuously. This mode can be used
* for use cases such as preview, auto-focus assist, still capture, or video recording.</p>
@@ -1690,21 +1720,21 @@
/**
* <p>Compression quality of the final JPEG
- * image</p>
- * <p>85-95 is typical usage range</p>
+ * image.</p>
+ * <p>85-95 is typical usage range.</p>
*/
public static final Key<Byte> JPEG_QUALITY =
new Key<Byte>("android.jpeg.quality", byte.class);
/**
* <p>Compression quality of JPEG
- * thumbnail</p>
+ * thumbnail.</p>
*/
public static final Key<Byte> JPEG_THUMBNAIL_QUALITY =
new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
/**
- * <p>Resolution of embedded JPEG thumbnail</p>
+ * <p>Resolution of embedded JPEG thumbnail.</p>
* <p>When set to (0, 0) value, the JPEG EXIF will not contain thumbnail,
* but the captured JPEG will still be a valid image.</p>
* <p>When a jpeg image capture is issued, the thumbnail size selected should have
@@ -1792,7 +1822,7 @@
/**
* <p>Distance to plane of sharpest focus,
- * measured from frontmost surface of the lens</p>
+ * measured from frontmost surface of the lens.</p>
* <p>Should be zero for fixed-focus cameras</p>
*/
public static final Key<Float> LENS_FOCUS_DISTANCE =
@@ -1800,7 +1830,7 @@
/**
* <p>The range of scene distances that are in
- * sharp focus (depth of field)</p>
+ * sharp focus (depth of field).</p>
* <p>If variable focus not supported, can still report
* fixed depth of field range</p>
*/
@@ -1810,12 +1840,18 @@
/**
* <p>Sets whether the camera device uses optical image stabilization (OIS)
* when capturing images.</p>
- * <p>OIS is used to compensate for motion blur due to small movements of
- * the camera during capture. Unlike digital image stabilization, OIS makes
- * use of mechanical elements to stabilize the camera sensor, and thus
- * allows for longer exposure times before camera shake becomes
- * apparent.</p>
- * <p>This is not expected to be supported on most devices.</p>
+ * <p>OIS is used to compensate for motion blur due to small
+ * movements of the camera during capture. Unlike digital image
+ * stabilization ({@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode}), OIS
+ * makes use of mechanical elements to stabilize the camera
+ * sensor, and thus allows for longer exposure times before
+ * camera shake becomes apparent.</p>
+ * <p>Not all devices will support OIS; see
+ * {@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization} for
+ * available controls.</p>
+ *
+ * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
+ * @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION
* @see #LENS_OPTICAL_STABILIZATION_MODE_OFF
* @see #LENS_OPTICAL_STABILIZATION_MODE_ON
*/
@@ -1859,16 +1895,15 @@
new Key<Integer>("android.lens.state", int.class);
/**
- * <p>Mode of operation for the noise reduction
- * algorithm</p>
+ * <p>Mode of operation for the noise reduction algorithm.</p>
* <p>Noise filtering control. OFF means no noise reduction
* will be applied by the camera device.</p>
- * <p>This must be set to a valid mode in
+ * <p>This must be set to a valid mode from
* {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}.</p>
* <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering
* will be applied. HIGH_QUALITY mode indicates that the camera device
* will use the highest-quality noise filtering algorithms,
- * even if it slows down capture rate. FAST means the camera device should not
+ * even if it slows down capture rate. FAST means the camera device will not
* slow down capture rate when applying noise filtering.</p>
*
* @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
@@ -1934,40 +1969,39 @@
new Key<Byte>("android.request.pipelineDepth", byte.class);
/**
- * <p>(x, y, width, height).</p>
- * <p>A rectangle with the top-level corner of (x,y) and size
- * (width, height). The region of the sensor that is used for
- * output. Each stream must use this rectangle to produce its
- * output, cropping to a smaller region if necessary to
- * maintain the stream's aspect ratio.</p>
- * <p>HAL2.x uses only (x, y, width)</p>
- * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
- * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage,
- * it is not croppable. The crop region will be ignored by raw streams.</p>
+ * <p>The region of the sensor to read out for this capture.</p>
+ * <p>The crop region coordinate system is based off
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the
+ * top-left corner of the sensor active array.</p>
+ * <p>Output streams use this rectangle to produce their output,
+ * cropping to a smaller region if necessary to maintain the
+ * stream's aspect ratio, then scaling the sensor input to
+ * match the output's configured resolution.</p>
+ * <p>The crop region is applied after the RAW to other color
+ * space (e.g. YUV) conversion. Since raw streams
+ * (e.g. RAW16) don't have the conversion stage, they are not
+ * croppable. The crop region will be ignored by raw streams.</p>
* <p>For non-raw streams, any additional per-stream cropping will
* be done to maximize the final pixel area of the stream.</p>
* <p>For example, if the crop region is set to a 4:3 aspect
- * ratio, then 4:3 streams should use the exact crop
- * region. 16:9 streams should further crop vertically
+ * ratio, then 4:3 streams will use the exact crop
+ * region. 16:9 streams will further crop vertically
* (letterbox).</p>
* <p>Conversely, if the crop region is set to a 16:9, then 4:3
- * outputs should crop horizontally (pillarbox), and 16:9
- * streams should match exactly. These additional crops must
+ * outputs will crop horizontally (pillarbox), and 16:9
+ * streams will match exactly. These additional crops will
* be centered within the crop region.</p>
- * <p>The output streams must maintain square pixels at all
- * times, no matter what the relative aspect ratios of the
- * crop region and the stream are. Negative values for
- * corner are allowed for raw output if full pixel array is
- * larger than active pixel array. Width and height may be
- * rounded to nearest larger supportable width, especially
- * for raw output, where only a few fixed scales may be
- * possible. The width and height of the crop region cannot
- * be set to be smaller than floor( activeArraySize.width /
- * {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} ) and floor(
- * activeArraySize.height /
- * {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom}), respectively.</p>
+ * <p>The width and height of the crop region cannot
+ * be set to be smaller than
+ * <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
+ * <code>floor( activeArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>, respectively.</p>
+ * <p>The camera device may adjust the crop region to account
+ * for rounding and other hardware requirements; the final
+ * crop region used will be included in the output capture
+ * result.</p>
*
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
*/
public static final Key<android.graphics.Rect> SCALER_CROP_REGION =
new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
@@ -2060,21 +2094,35 @@
new Key<Long>("android.sensor.frameDuration", long.class);
/**
- * <p>Gain applied to image data. Must be
- * implemented through analog gain only if set to values
- * below 'maximum analog sensitivity'.</p>
- * <p>If the sensor can't apply this exact gain, it should lessen the
- * gain to the nearest possible value (rather than gain more).</p>
- * <p>ISO 12232:2006 REI method</p>
+ * <p>The amount of gain applied to sensor data
+ * before processing.</p>
+ * <p>The sensitivity is the standard ISO sensitivity value,
+ * as defined in ISO 12232:2006.</p>
+ * <p>The sensitivity must be within {@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}, and
+ * if if it less than {@link CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY android.sensor.maxAnalogSensitivity}, the camera device
+ * is guaranteed to use only analog amplification for applying the gain.</p>
+ * <p>If the camera device cannot apply the exact sensitivity
+ * requested, it will reduce the gain to the nearest supported
+ * value. The final sensitivity used will be available in the
+ * output capture result.</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE
+ * @see CameraCharacteristics#SENSOR_MAX_ANALOG_SENSITIVITY
*/
public static final Key<Integer> SENSOR_SENSITIVITY =
new Key<Integer>("android.sensor.sensitivity", int.class);
/**
* <p>Time at start of exposure of first
- * row</p>
- * <p>Monotonic, should be synced to other timestamps in
- * system</p>
+ * row of the image sensor, in nanoseconds.</p>
+ * <p>The timestamps are also included in all image
+ * buffers produced for the same capture, and will be identical
+ * on all the outputs. The timestamps measure time since an
+ * unspecified starting point, and are monotonically
+ * increasing.</p>
+ * <p>They can be compared with the timestamps for other captures
+ * from the same camera device, but are not guaranteed to be
+ * comparable to any other time source.</p>
*/
public static final Key<Long> SENSOR_TIMESTAMP =
new Key<Long>("android.sensor.timestamp", long.class);
@@ -2150,7 +2198,7 @@
* <p>When enabled, the sensor sends a test pattern instead of
* doing a real exposure from the camera.</p>
* <p>When a test pattern is enabled, all manual sensor controls specified
- * by android.sensor.* should be ignored. All other controls should
+ * by android.sensor.* will be ignored. All other controls should
* work as normal.</p>
* <p>For example, if manual flash is enabled, flash firing should still
* occur (and that the test pattern remain unmodified, since the flash
@@ -2188,7 +2236,7 @@
* lens shading map data in android.statistics.lensShadingMap, with size specified
* by android.lens.info.shadingMapSize; the returned shading map data will be the one
* applied by the camera device for this capture request.</p>
- * <p>The shading map data may depend on the AE and AWB statistics, therefore the reliability
+ * <p>The shading map data may depend on the auto-exposure (AE) and AWB statistics, therefore the reliability
* of the map data may be affected by the AE and AWB algorithms. When AE and AWB are in
* AUTO modes({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF and {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} <code>!=</code> OFF),
* to get best results, it is recommended that the applications wait for the AE and AWB to
@@ -2205,8 +2253,8 @@
new Key<Integer>("android.shading.mode", int.class);
/**
- * <p>State of the face detector
- * unit</p>
+ * <p>Control for the face detector
+ * unit.</p>
* <p>Whether face detection is enabled, and whether it
* should output just the basic fields or the full set of
* fields. Value must be one of the
@@ -2221,9 +2269,13 @@
new Key<Integer>("android.statistics.faceDetectMode", int.class);
/**
- * <p>List of unique IDs for detected
- * faces</p>
- * <p>Only available if faceDetectMode == FULL</p>
+ * <p>List of unique IDs for detected faces.</p>
+ * <p>Each detected face is given a unique ID that is valid for as long as the face is visible
+ * to the camera device. A face that leaves the field of view and later returns may be
+ * assigned a new ID.</p>
+ * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL</p>
+ *
+ * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @hide
*/
public static final Key<int[]> STATISTICS_FACE_IDS =
@@ -2231,8 +2283,13 @@
/**
* <p>List of landmarks for detected
- * faces</p>
- * <p>Only available if faceDetectMode == FULL</p>
+ * faces.</p>
+ * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
+ * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @hide
*/
public static final Key<int[]> STATISTICS_FACE_LANDMARKS =
@@ -2240,8 +2297,13 @@
/**
* <p>List of the bounding rectangles for detected
- * faces</p>
- * <p>Only available if faceDetectMode != OFF</p>
+ * faces.</p>
+ * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
+ * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
+ * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF</p>
+ *
+ * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
+ * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @hide
*/
public static final Key<android.graphics.Rect[]> STATISTICS_FACE_RECTANGLES =
@@ -2250,8 +2312,9 @@
/**
* <p>List of the face confidence scores for
* detected faces</p>
- * <p>Only available if faceDetectMode != OFF. The value should be
- * meaningful (for example, setting 100 at all times is illegal).</p>
+ * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF.</p>
+ *
+ * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @hide
*/
public static final Key<byte[]> STATISTICS_FACE_SCORES =
@@ -2370,7 +2433,7 @@
* applied to that frame.</p>
* <p>The 4 channel gains are defined in Bayer domain,
* see {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} for details.</p>
- * <p>This value should always be calculated by the AWB block,
+ * <p>This value should always be calculated by the auto-white balance (AWB) block,
* regardless of the android.control.* current values.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
@@ -2396,7 +2459,7 @@
* that frame.</p>
* <p>These estimates must be provided for all frames, even if
* capture settings and color transforms are set by the application.</p>
- * <p>This value should always be calculated by the AWB block,
+ * <p>This value should always be calculated by the auto-white balance (AWB) block,
* regardless of the android.control.* current values.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* @deprecated
@@ -2415,12 +2478,13 @@
* The camera device uses this entry to tell the application what the scene
* illuminant frequency is.</p>
* <p>When manual exposure control is enabled
- * (<code>{@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} == OFF</code> or <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == OFF</code>),
- * the {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} doesn't do the antibanding, and the
- * application can ensure it selects exposure times that do not cause banding
- * issues by looking into this metadata field. See {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode}
- * for more details.</p>
- * <p>Report NONE if there doesn't appear to be flickering illumination.</p>
+ * (<code>{@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} == OFF</code> or <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} ==
+ * OFF</code>), the {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} doesn't perform
+ * antibanding, and the application can ensure it selects
+ * exposure times that do not cause banding issues by looking
+ * into this metadata field. See
+ * {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} for more details.</p>
+ * <p>Reports NONE if there doesn't appear to be flickering illumination.</p>
*
* @see CaptureRequest#CONTROL_AE_ANTIBANDING_MODE
* @see CaptureRequest#CONTROL_AE_MODE
@@ -2435,7 +2499,7 @@
/**
* <p>Operating mode for hotpixel map generation.</p>
* <p>If set to ON, a hotpixel map is returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.
- * If set to OFF, no hotpixel map should be returned.</p>
+ * If set to OFF, no hotpixel map will be returned.</p>
* <p>This must be set to a valid mode from {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES android.statistics.info.availableHotPixelMapModes}.</p>
*
* @see CaptureResult#STATISTICS_HOT_PIXEL_MAP
@@ -2463,7 +2527,7 @@
* <p>Whether the camera device will output the lens
* shading map in output result metadata.</p>
* <p>When set to ON,
- * android.statistics.lensShadingMap must be provided in
+ * android.statistics.lensShadingMap will be provided in
* the output result metadata.</p>
* @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
* @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index 54568ed..3e3303c 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -22,12 +22,16 @@
import android.location.Location;
import android.media.ExifInterface;
import android.media.Image;
+import android.os.SystemClock;
import android.util.Size;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
/**
* The {@link DngCreator} class provides functions to write raw pixel data as a DNG file.
@@ -55,6 +59,7 @@
*/
public final class DngCreator implements AutoCloseable {
+ private static final String TAG = "DngCreator";
/**
* Create a new DNG object.
*
@@ -75,7 +80,25 @@
if (characteristics == null || metadata == null) {
throw new NullPointerException("Null argument to DngCreator constructor");
}
- nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy());
+
+ // Find current time
+ long currentTime = System.currentTimeMillis();
+
+ // Find boot time
+ long bootTimeMillis = currentTime - SystemClock.elapsedRealtime();
+
+ // Find capture time (nanos since boot)
+ Long timestamp = metadata.get(CaptureResult.SENSOR_TIMESTAMP);
+ long captureTime = currentTime;
+ if (timestamp != null) {
+ captureTime = timestamp / 1000000 + bootTimeMillis;
+ }
+
+ // Format for metadata
+ String formattedCaptureTime = sDateTimeStampFormat.format(captureTime);
+
+ nativeInit(characteristics.getNativeCopy(), metadata.getNativeCopy(),
+ formattedCaptureTime);
}
/**
@@ -98,6 +121,7 @@
* <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_270}</li>
* </ul>
* @return this {@link #DngCreator} object.
+ * @hide
*/
public DngCreator setOrientation(int orientation) {
@@ -124,6 +148,7 @@
*
* @param pixels a {@link android.graphics.Bitmap} of pixel data.
* @return this {@link #DngCreator} object.
+ * @hide
*/
public DngCreator setThumbnail(Bitmap pixels) {
if (pixels == null) {
@@ -157,6 +182,7 @@
* @param pixels an {@link android.media.Image} object with the format
* {@link android.graphics.ImageFormat#YUV_420_888}.
* @return this {@link #DngCreator} object.
+ * @hide
*/
public DngCreator setThumbnail(Image pixels) {
if (pixels == null) {
@@ -193,6 +219,7 @@
*
* @throws java.lang.IllegalArgumentException if the given location object doesn't
* contain enough information to set location metadata.
+ * @hide
*/
public DngCreator setLocation(Location location) {
/*TODO*/
@@ -208,6 +235,7 @@
*
* @param description the user description string.
* @return this {@link #DngCreator} object.
+ * @hide
*/
public DngCreator setDescription(String description) {
/*TODO*/
@@ -240,6 +268,7 @@
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
* @throws java.lang.IllegalArgumentException if the size passed in does not match the
+ * @hide
*/
public void writeInputStream(OutputStream dngOutput, Size size, InputStream pixels, long offset)
throws IOException {
@@ -274,6 +303,7 @@
* @throws IOException if an error was encountered in the input or output stream.
* @throws java.lang.IllegalStateException if not enough metadata information has been
* set to write a well-formatted DNG file.
+ * @hide
*/
public void writeByteBuffer(OutputStream dngOutput, Size size, ByteBuffer pixels, long offset)
throws IOException {
@@ -329,6 +359,13 @@
}
}
+ private static final String TIFF_DATETIME_FORMAT = "yyyy:MM:dd kk:mm:ss";
+ private static final DateFormat sDateTimeStampFormat =
+ new SimpleDateFormat(TIFF_DATETIME_FORMAT);
+
+ static {
+ sDateTimeStampFormat.setTimeZone(TimeZone.getDefault());
+ }
/**
* This field is used by native code, do not access or modify.
*/
@@ -337,7 +374,8 @@
private static native void nativeClassInit();
private synchronized native void nativeInit(CameraMetadataNative nativeCharacteristics,
- CameraMetadataNative nativeResult);
+ CameraMetadataNative nativeResult,
+ String captureTime);
private synchronized native void nativeDestroy();
diff --git a/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.java
new file mode 100644
index 0000000..866f370
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/ArgumentReplacingDispatcher.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.hardware.camera2.dispatch;
+
+import java.lang.reflect.Method;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * A dispatcher that replaces one argument with another; replaces any argument at an index
+ * with another argument.
+ *
+ * <p>For example, we can override an {@code void onSomething(int x)} calls to have {@code x} always
+ * equal to 1. Or, if using this with a duck typing dispatcher, we could even overwrite {@code x} to
+ * be something
+ * that's not an {@code int}.</p>
+ *
+ * @param <T>
+ * source dispatch type, whose methods with {@link #dispatch} will be called
+ * @param <TArg>
+ * argument replacement type, args in {@link #dispatch} matching {@code argumentIndex}
+ * will be overriden to objects of this type
+ */
+public class ArgumentReplacingDispatcher<T, TArg> implements Dispatchable<T> {
+
+ private final Dispatchable<T> mTarget;
+ private final int mArgumentIndex;
+ private final TArg mReplaceWith;
+
+ /**
+ * Create a new argument replacing dispatcher; dispatches are forwarded to {@code target}
+ * after the argument is replaced.
+ *
+ * <p>For example, if a method {@code onAction(T1 a, Integer b, T2 c)} is invoked, and we wanted
+ * to replace all occurrences of {@code b} with {@code 0xDEADBEEF}, we would set
+ * {@code argumentIndex = 1} and {@code replaceWith = 0xDEADBEEF}.</p>
+ *
+ * <p>If a method dispatched has less arguments than {@code argumentIndex}, it is
+ * passed through with the arguments unchanged.</p>
+ *
+ * @param target destination dispatch type, methods will be redirected to this dispatcher
+ * @param argumentIndex the numeric index of the argument {@code >= 0}
+ * @param replaceWith arguments matching {@code argumentIndex} will be replaced with this object
+ */
+ public ArgumentReplacingDispatcher(Dispatchable<T> target, int argumentIndex,
+ TArg replaceWith) {
+ mTarget = checkNotNull(target, "target must not be null");
+ mArgumentIndex = checkArgumentNonnegative(argumentIndex,
+ "argumentIndex must not be negative");
+ mReplaceWith = checkNotNull(replaceWith, "replaceWith must not be null");
+ }
+
+ @Override
+ public Object dispatch(Method method, Object[] args) throws Throwable {
+
+ if (args.length > mArgumentIndex) {
+ args = arrayCopy(args); // don't change in-place since it can affect upstream dispatches
+ args[mArgumentIndex] = mReplaceWith;
+ }
+
+ return mTarget.dispatch(method, args);
+ }
+
+ private static Object[] arrayCopy(Object[] array) {
+ int length = array.length;
+ Object[] newArray = new Object[length];
+ for (int i = 0; i < length; ++i) {
+ newArray[i] = array[i];
+ }
+ return newArray;
+ }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.java
new file mode 100644
index 0000000..fe575b2
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/BroadcastDispatcher.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 android.hardware.camera2.dispatch;
+
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Broadcast a single dispatch into multiple other dispatchables.
+ *
+ * <p>Every time {@link #dispatch} is invoked, all the broadcast targets will
+ * see the same dispatch as well. The first target's return value is returned.</p>
+ *
+ * <p>This enables a single listener to be converted into a multi-listener.</p>
+ */
+public class BroadcastDispatcher<T> implements Dispatchable<T> {
+
+ private final List<Dispatchable<T>> mDispatchTargets;
+
+ /**
+ * Create a broadcast dispatcher from the supplied dispatch targets.
+ *
+ * @param dispatchTargets one or more targets to dispatch to
+ */
+ @SafeVarargs
+ public BroadcastDispatcher(Dispatchable<T>... dispatchTargets) {
+ mDispatchTargets = Arrays.asList(
+ checkNotNull(dispatchTargets, "dispatchTargets must not be null"));
+ }
+
+ @Override
+ public Object dispatch(Method method, Object[] args) throws Throwable {
+ Object result = null;
+ boolean gotResult = false;
+
+ for (Dispatchable<T> dispatchTarget : mDispatchTargets) {
+ Object localResult = dispatchTarget.dispatch(method, args);
+
+ if (!gotResult) {
+ gotResult = true;
+ result = localResult;
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/Dispatchable.java b/core/java/android/hardware/camera2/dispatch/Dispatchable.java
new file mode 100644
index 0000000..753103f
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/Dispatchable.java
@@ -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.
+ */
+package android.hardware.camera2.dispatch;
+
+import java.lang.reflect.Method;
+
+/**
+ * Dynamically dispatch a method and its argument to some object.
+ *
+ * <p>This can be used to intercept method calls and do work around them, redirect work,
+ * or block calls entirely.</p>
+ */
+public interface Dispatchable<T> {
+ /**
+ * Dispatch the method and arguments to this object.
+ * @param method a method defined in class {@code T}
+ * @param args arguments corresponding to said {@code method}
+ * @return the object returned when invoking {@code method}
+ * @throws Throwable any exception that might have been raised while invoking the method
+ */
+ public Object dispatch(Method method, Object[] args) throws Throwable;
+}
diff --git a/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
new file mode 100644
index 0000000..75f97e4
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/DuckTypingDispatcher.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+
+import java.lang.reflect.Method;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Duck typing dispatcher; converts dispatch methods calls from one class to another by
+ * looking up equivalently methods at runtime by name.
+ *
+ * <p>For example, if two types have identical method names and arguments, but
+ * are not subclasses/subinterfaces of each other, this dispatcher will allow calls to be
+ * made from one type to the other.</p>
+ *
+ * @param <TFrom> source dispatch type, whose methods with {@link #dispatch} will be called
+ * @param <T> destination dispatch type, methods will be converted to the class of {@code T}
+ */
+public class DuckTypingDispatcher<TFrom, T> implements Dispatchable<TFrom> {
+
+ private final MethodNameInvoker<T> mDuck;
+
+ /**
+ * Create a new duck typing dispatcher.
+ *
+ * @param target destination dispatch type, methods will be redirected to this dispatcher
+ * @param targetClass destination dispatch class, methods will be converted to this class's
+ */
+ public DuckTypingDispatcher(Dispatchable<T> target, Class<T> targetClass) {
+ checkNotNull(targetClass, "targetClass must not be null");
+ checkNotNull(target, "target must not be null");
+
+ mDuck = new MethodNameInvoker<T>(target, targetClass);
+ }
+
+ @Override
+ public Object dispatch(Method method, Object[] args) {
+ return mDuck.invoke(method.getName(), args);
+ }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.java
new file mode 100644
index 0000000..f8e9d49
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/HandlerDispatcher.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.hardware.camera2.dispatch;
+
+import android.hardware.camera2.utils.UncheckedThrow;
+import android.os.Handler;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Forward all interface calls into a handler by posting it as a {@code Runnable}.
+ *
+ * <p>All calls will return immediately; functions with return values will return a default
+ * value of {@code null}, {@code 0}, or {@code false} where that value is legal.</p>
+ *
+ * <p>Any exceptions thrown on the handler while trying to invoke a method
+ * will be re-thrown. Throwing checked exceptions on a handler which doesn't expect any
+ * checked exceptions to be thrown will result in "undefined" behavior
+ * (although in practice it is usually thrown as normal).</p>
+ */
+public class HandlerDispatcher<T> implements Dispatchable<T> {
+
+ private static final String TAG = "HandlerDispatcher";
+
+ private final Dispatchable<T> mDispatchTarget;
+ private final Handler mHandler;
+
+ /**
+ * Create a dispatcher that forwards it's dispatch calls by posting
+ * them onto the {@code handler} as a {@code Runnable}.
+ *
+ * @param dispatchTarget the destination whose method calls will be redirected into the handler
+ * @param handler all calls into {@code dispatchTarget} will be posted onto this handler
+ * @param <T> the type of the element you want to wrap.
+ * @return a dispatcher that will forward it's dispatch calls to a handler
+ */
+ public HandlerDispatcher(Dispatchable<T> dispatchTarget, Handler handler) {
+ mDispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
+ mHandler = checkNotNull(handler, "handler must not be null");
+ }
+
+ @Override
+ public Object dispatch(final Method method, final Object[] args) throws Throwable {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mDispatchTarget.dispatch(method, args);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ // Potential UB. Hopefully 't' is a runtime exception.
+ UncheckedThrow.throwAnyException(t);
+ } catch (IllegalAccessException e) {
+ // Impossible
+ Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
+ } catch (IllegalArgumentException e) {
+ // Impossible
+ Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
+ } catch (Throwable e) {
+ UncheckedThrow.throwAnyException(e);
+ }
+ }
+ });
+
+ // TODO handle primitive return values that would avoid NPE if unboxed
+ return null;
+ }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
new file mode 100644
index 0000000..ac5f526
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/InvokeDispatcher.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+import android.hardware.camera2.utils.UncheckedThrow;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import static com.android.internal.util.Preconditions.*;
+
+
+public class InvokeDispatcher<T> implements Dispatchable<T> {
+
+ private static final String TAG = "InvocationSink";
+ private final T mTarget;
+
+ public InvokeDispatcher(T target) {
+ mTarget = checkNotNull(target, "target must not be null");
+ }
+
+ @Override
+ public Object dispatch(Method method, Object[] args) {
+ try {
+ return method.invoke(mTarget, args);
+ } catch (InvocationTargetException e) {
+ Throwable t = e.getTargetException();
+ // Potential UB. Hopefully 't' is a runtime exception.
+ UncheckedThrow.throwAnyException(t);
+ } catch (IllegalAccessException e) {
+ // Impossible
+ Log.wtf(TAG, "IllegalAccessException while invoking " + method, e);
+ } catch (IllegalArgumentException e) {
+ // Impossible
+ Log.wtf(TAG, "IllegalArgumentException while invoking " + method, e);
+ }
+
+ // unreachable
+ return null;
+ }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
new file mode 100644
index 0000000..02c3d87
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/MethodNameInvoker.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+import android.hardware.camera2.utils.UncheckedThrow;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Invoke a method on a dispatchable by its name (without knowing the {@code Method} ahead of time).
+ *
+ * @param <T> destination dispatch type, methods will be looked up in the class of {@code T}
+ */
+public class MethodNameInvoker<T> {
+
+ private final Dispatchable<T> mTarget;
+ private final Class<T> mTargetClass;
+ private final ConcurrentHashMap<String, Method> mMethods =
+ new ConcurrentHashMap<>();
+
+ /**
+ * Create a new method name invoker.
+ *
+ * @param target destination dispatch type, invokes will be redirected to this dispatcher
+ * @param targetClass destination dispatch class, the invoked methods will be from this class
+ */
+ public MethodNameInvoker(Dispatchable<T> target, Class<T> targetClass) {
+ mTargetClass = targetClass;
+ mTarget = target;
+ }
+
+ /**
+ * Invoke a method by its name.
+ *
+ * <p>If more than one method exists in {@code targetClass}, the first method will be used.</p>
+ *
+ * @param methodName
+ * The name of the method, which will be matched 1:1 to the destination method
+ * @param params
+ * Variadic parameter list.
+ * @return
+ * The same kind of value that would normally be returned by calling {@code methodName}
+ * statically.
+ *
+ * @throws IllegalArgumentException if {@code methodName} does not exist on the target class
+ * @throws Throwable will rethrow anything that the target method would normally throw
+ */
+ @SuppressWarnings("unchecked")
+ public <K> K invoke(String methodName, Object... params) {
+ checkNotNull(methodName, "methodName must not be null");
+
+ Method targetMethod = mMethods.get(methodName);
+ if (targetMethod == null) {
+ for (Method method : mTargetClass.getMethods()) {
+ // TODO future: match by # of params and types of params if possible
+ if (method.getName().equals(methodName)) {
+ targetMethod = method;
+ mMethods.put(methodName, targetMethod);
+ break;
+ }
+ }
+
+ if (targetMethod == null) {
+ throw new IllegalArgumentException(
+ "Method " + methodName + " does not exist on class " + mTargetClass);
+ }
+ }
+
+ try {
+ return (K) mTarget.dispatch(targetMethod, params);
+ } catch (Throwable e) {
+ UncheckedThrow.throwAnyException(e);
+ // unreachable
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/NullDispatcher.java b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
new file mode 100644
index 0000000..fada075
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/NullDispatcher.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.dispatch;
+
+
+import java.lang.reflect.Method;
+
+/**
+ * Do nothing when dispatching; follows the null object pattern.
+ */
+public class NullDispatcher<T> implements Dispatchable<T> {
+ /**
+ * Create a dispatcher that does nothing when dispatched to.
+ */
+ public NullDispatcher() {
+ }
+
+ /**
+ * Do nothing; all parameters are ignored.
+ */
+ @Override
+ public Object dispatch(Method method, Object[] args) {
+ return null;
+ }
+}
diff --git a/core/java/android/hardware/camera2/dispatch/package.html b/core/java/android/hardware/camera2/dispatch/package.html
new file mode 100644
index 0000000..783d0a1
--- /dev/null
+++ b/core/java/android/hardware/camera2/dispatch/package.html
@@ -0,0 +1,3 @@
+<body>
+{@hide}
+</body>
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
new file mode 100644
index 0000000..f829f5e
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.impl;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.dispatch.ArgumentReplacingDispatcher;
+import android.hardware.camera2.dispatch.BroadcastDispatcher;
+import android.hardware.camera2.dispatch.Dispatchable;
+import android.hardware.camera2.dispatch.DuckTypingDispatcher;
+import android.hardware.camera2.dispatch.HandlerDispatcher;
+import android.hardware.camera2.dispatch.InvokeDispatcher;
+import android.hardware.camera2.dispatch.NullDispatcher;
+import android.hardware.camera2.utils.TaskDrainer;
+import android.hardware.camera2.utils.TaskSingleDrainer;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
+import static com.android.internal.util.Preconditions.*;
+
+public class CameraCaptureSessionImpl extends CameraCaptureSession {
+ private static final String TAG = "CameraCaptureSession";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ /** User-specified set of surfaces used as the configuration outputs */
+ private final List<Surface> mOutputs;
+ /**
+ * User-specified state listener, used for outgoing events; calls to this object will be
+ * automatically {@link Handler#post(Runnable) posted} to {@code mStateHandler}.
+ */
+ private final CameraCaptureSession.StateListener mStateListener;
+ /** User-specified state handler used for outgoing state listener events */
+ private final Handler mStateHandler;
+
+ /** Internal camera device; used to translate calls into existing deprecated API */
+ private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
+ /** Internal handler; used for all incoming events to preserve total order */
+ private final Handler mDeviceHandler;
+
+ /** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
+ private final TaskDrainer<Integer> mSequenceDrainer;
+ /** Drain state transitions from ACTIVE -> IDLE */
+ private final TaskSingleDrainer mIdleDrainer;
+ /** Drain state transitions from BUSY -> IDLE */
+ private final TaskSingleDrainer mAbortDrainer;
+ /** Drain the UNCONFIGURED state transition */
+ private final TaskSingleDrainer mUnconfigureDrainer;
+
+ /** This session is closed; all further calls will throw ISE */
+ private boolean mClosed = false;
+ /** Do not unconfigure if this is set; another session will overwrite configuration */
+ private boolean mSkipUnconfigure = false;
+
+ /** Is the session in the process of aborting? Pay attention to BUSY->IDLE transitions. */
+ private boolean mAborting;
+
+ /**
+ * Create a new CameraCaptureSession.
+ *
+ * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
+ * There must be no pending actions
+ * (e.g. no pending captures, no repeating requests, no flush).</p>
+ */
+ CameraCaptureSessionImpl(List<Surface> outputs,
+ CameraCaptureSession.StateListener listener, Handler stateHandler,
+ android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
+ Handler deviceStateHandler, boolean configureSuccess) {
+ if (outputs == null || outputs.isEmpty()) {
+ throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
+ } else if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+
+ // TODO: extra verification of outputs
+ mOutputs = outputs;
+ mStateHandler = checkHandler(stateHandler);
+ mStateListener = createUserStateListenerProxy(mStateHandler, listener);
+
+ mDeviceHandler = checkNotNull(deviceStateHandler, "deviceStateHandler must not be null");
+ mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");
+
+ /*
+ * Use the same handler as the device's StateListener for all the internal coming events
+ *
+ * This ensures total ordering between CameraDevice.StateListener and
+ * CameraDevice.CaptureListener events.
+ */
+ mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(),
+ /*name*/"seq");
+ mIdleDrainer = new TaskSingleDrainer(mDeviceHandler, new IdleDrainListener(),
+ /*name*/"idle");
+ mAbortDrainer = new TaskSingleDrainer(mDeviceHandler, new AbortDrainListener(),
+ /*name*/"abort");
+ mUnconfigureDrainer = new TaskSingleDrainer(mDeviceHandler, new UnconfigureDrainListener(),
+ /*name*/"unconf");
+
+ // CameraDevice should call configureOutputs and have it finish before constructing us
+
+ if (configureSuccess) {
+ mStateListener.onConfigured(this);
+ if (VERBOSE) Log.v(TAG, "ctor - Created session successfully");
+ } else {
+ mStateListener.onConfigureFailed(this);
+ mClosed = true; // do not fire any other callbacks, do not allow any other work
+ Log.e(TAG, "Failed to create capture session; configuration failed");
+ }
+ }
+
+ @Override
+ public CameraDevice getDevice() {
+ return mDeviceImpl;
+ }
+
+ @Override
+ public synchronized int capture(CaptureRequest request, CaptureListener listener,
+ Handler handler) throws CameraAccessException {
+ if (request == null) {
+ throw new IllegalArgumentException("request must not be null");
+ }
+
+ checkNotClosed();
+ checkLegalToCapture();
+
+ handler = checkHandler(handler);
+
+ if (VERBOSE) {
+ Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" +
+ " " + handler);
+ }
+
+ return addPendingSequence(mDeviceImpl.capture(request,
+ createCaptureListenerProxy(handler, listener), mDeviceHandler));
+ }
+
+ @Override
+ public synchronized int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+ Handler handler) throws CameraAccessException {
+ if (requests == null) {
+ throw new IllegalArgumentException("requests must not be null");
+ } else if (requests.isEmpty()) {
+ throw new IllegalArgumentException("requests must have at least one element");
+ }
+
+ checkNotClosed();
+ checkLegalToCapture();
+
+ handler = checkHandler(handler);
+
+ if (VERBOSE) {
+ CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+ Log.v(TAG, "captureBurst - requests " + Arrays.toString(requestArray) + ", listener " +
+ listener + " handler" + "" + handler);
+ }
+
+ return addPendingSequence(mDeviceImpl.captureBurst(requests,
+ createCaptureListenerProxy(handler, listener), mDeviceHandler));
+ }
+
+ @Override
+ public synchronized int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+ Handler handler) throws CameraAccessException {
+ if (request == null) {
+ throw new IllegalArgumentException("request must not be null");
+ }
+
+ checkNotClosed();
+ checkLegalToCapture();
+
+ handler = checkHandler(handler);
+
+ if (VERBOSE) {
+ Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener +
+ " handler" + " " + handler);
+ }
+
+ return addPendingSequence(mDeviceImpl.setRepeatingRequest(request,
+ createCaptureListenerProxy(handler, listener), mDeviceHandler));
+ }
+
+ @Override
+ public synchronized int setRepeatingBurst(List<CaptureRequest> requests,
+ CaptureListener listener, Handler handler) throws CameraAccessException {
+ if (requests == null) {
+ throw new IllegalArgumentException("requests must not be null");
+ } else if (requests.isEmpty()) {
+ throw new IllegalArgumentException("requests must have at least one element");
+ }
+
+ checkNotClosed();
+ checkLegalToCapture();
+
+ handler = checkHandler(handler);
+
+ if (VERBOSE) {
+ CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]);
+ Log.v(TAG, "setRepeatingBurst - requests " + Arrays.toString(requestArray) +
+ ", listener " + listener + " handler" + "" + handler);
+ }
+
+ return addPendingSequence(mDeviceImpl.setRepeatingBurst(requests,
+ createCaptureListenerProxy(handler, listener), mDeviceHandler));
+ }
+
+ @Override
+ public synchronized void stopRepeating() throws CameraAccessException {
+ checkNotClosed();
+
+ if (VERBOSE) {
+ Log.v(TAG, "stopRepeating");
+ }
+
+ mDeviceImpl.stopRepeating();
+ }
+
+ @Override
+ public synchronized void abortCaptures() throws CameraAccessException {
+ checkNotClosed();
+
+ if (VERBOSE) {
+ Log.v(TAG, "abortCaptures");
+ }
+
+ if (mAborting) {
+ Log.w(TAG, "abortCaptures - Session is already aborting; doing nothing");
+ return;
+ }
+
+ mAborting = true;
+ mAbortDrainer.taskStarted();
+
+ mDeviceImpl.flush();
+ // The next BUSY -> IDLE set of transitions will mark the end of the abort.
+ }
+
+ /**
+ * Replace this session with another session.
+ *
+ * <p>This is an optimization to avoid unconfiguring and then immediately having to
+ * reconfigure again.</p>
+ *
+ * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
+ * <p>
+ *
+ * @see CameraCaptureSession#close
+ */
+ synchronized void replaceSessionClose(CameraCaptureSession other) {
+ /*
+ * In order for creating new sessions to be fast, the new session should be created
+ * before the old session is closed.
+ *
+ * Otherwise the old session will always unconfigure if there is no new session to
+ * replace it.
+ *
+ * Unconfiguring could add hundreds of milliseconds of delay. We could race and attempt
+ * to skip unconfigure if a new session is created before the captures are all drained,
+ * but this would introduce nondeterministic behavior.
+ */
+
+ if (VERBOSE) Log.v(TAG, "replaceSessionClose");
+
+ // #close was already called explicitly, keep going the slow route
+ if (mClosed) {
+ if (VERBOSE) Log.v(TAG, "replaceSessionClose - close was already called");
+ return;
+ }
+
+ mSkipUnconfigure = true;
+ close();
+ }
+
+ @Override
+ public synchronized void close() {
+
+ if (mClosed) {
+ if (VERBOSE) Log.v(TAG, "close - reentering");
+ return;
+ }
+
+ if (VERBOSE) Log.v(TAG, "close - first time");
+
+ mClosed = true;
+
+ /*
+ * Flush out any repeating request. Since camera is closed, no new requests
+ * can be queued, and eventually the entire request queue will be drained.
+ *
+ * If the camera device was already closed, short circuit and do nothing; since
+ * no more internal device callbacks will fire anyway.
+ *
+ * Otherwise, once stopRepeating is done, wait for camera to idle, then unconfigure the
+ * camera. Once that's done, fire #onClosed.
+ */
+ try {
+ mDeviceImpl.stopRepeating();
+ } catch (IllegalStateException e) {
+ // OK: Camera device may already be closed, nothing else to do
+ Log.w(TAG, "The camera device was already closed: ", e);
+
+ // TODO: Fire onClosed anytime we get the device onClosed or the ISE?
+ // or just suppress the ISE only and rely onClosed.
+ // Also skip any of the draining work if this is already closed.
+
+ // Short-circuit; queue listener immediately and return
+ mStateListener.onClosed(this);
+ return;
+ } catch (CameraAccessException e) {
+ // OK: close does not throw checked exceptions.
+ Log.e(TAG, "Exception while stopping repeating: ", e);
+
+ // TODO: call onError instead of onClosed if this happens
+ }
+
+ // If no sequences are pending, fire #onClosed immediately
+ mSequenceDrainer.beginDrain();
+ }
+
+ /**
+ * Post calls into a CameraCaptureSession.StateListener to the user-specified {@code handler}.
+ */
+ private StateListener createUserStateListenerProxy(Handler handler, StateListener listener) {
+ InvokeDispatcher<StateListener> userListenerSink = new InvokeDispatcher<>(listener);
+ HandlerDispatcher<StateListener> handlerPassthrough =
+ new HandlerDispatcher<>(userListenerSink, handler);
+
+ return new ListenerProxies.SessionStateListenerProxy(handlerPassthrough);
+ }
+
+ /**
+ * Forward callbacks from
+ * CameraDevice.CaptureListener to the CameraCaptureSession.CaptureListener.
+ *
+ * <p>In particular, all calls are automatically split to go both to our own
+ * internal listener, and to the user-specified listener (by transparently posting
+ * to the user-specified handler).</p>
+ *
+ * <p>When a capture sequence finishes, update the pending checked sequences set.</p>
+ */
+ @SuppressWarnings("deprecation")
+ private CameraDevice.CaptureListener createCaptureListenerProxy(
+ Handler handler, CaptureListener listener) {
+ CameraDevice.CaptureListener localListener = new CameraDevice.CaptureListener() {
+ @Override
+ public void onCaptureSequenceCompleted(CameraDevice camera,
+ int sequenceId, long frameNumber) {
+ finishPendingSequence(sequenceId);
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(CameraDevice camera,
+ int sequenceId) {
+ finishPendingSequence(sequenceId);
+ }
+ };
+
+ /*
+ * Split the calls from the device listener into local listener and the following chain:
+ * - replace the first CameraDevice arg with a CameraCaptureSession
+ * - duck type from device listener to session listener
+ * - then forward the call to a handler
+ * - then finally invoke the destination method on the session listener object
+ */
+ Dispatchable<CaptureListener> userListenerSink;
+ if (listener == null) { // OK: API allows the user to not specify a listener
+ userListenerSink = new NullDispatcher<>();
+ } else {
+ userListenerSink = new InvokeDispatcher<>(listener);
+ }
+
+ InvokeDispatcher<CameraDevice.CaptureListener> localSink =
+ new InvokeDispatcher<>(localListener);
+ HandlerDispatcher<CaptureListener> handlerPassthrough =
+ new HandlerDispatcher<>(userListenerSink, handler);
+ DuckTypingDispatcher<CameraDevice.CaptureListener, CaptureListener> duckToSession
+ = new DuckTypingDispatcher<>(handlerPassthrough, CaptureListener.class);
+ ArgumentReplacingDispatcher<CameraDevice.CaptureListener, CameraCaptureSessionImpl>
+ replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession,
+ /*argumentIndex*/0, this);
+
+ BroadcastDispatcher<CameraDevice.CaptureListener> broadcaster =
+ new BroadcastDispatcher<CameraDevice.CaptureListener>(
+ replaceDeviceWithSession,
+ localSink);
+
+ return new ListenerProxies.DeviceCaptureListenerProxy(broadcaster);
+ }
+
+ /**
+ *
+ * Create an internal state listener, to be invoked on the mDeviceHandler
+ *
+ * <p>It has a few behaviors:
+ * <ul>
+ * <li>Convert device state changes into session state changes.
+ * <li>Keep track of async tasks that the session began (idle, abort).
+ * </ul>
+ * </p>
+ * */
+ CameraDevice.StateListener getDeviceStateListener() {
+ final CameraCaptureSession session = this;
+
+ return new CameraDevice.StateListener() {
+ private boolean mBusy = false;
+ private boolean mActive = false;
+
+ @Override
+ public void onOpened(CameraDevice camera) {
+ throw new AssertionError("Camera must already be open before creating a session");
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ close();
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ // TODO: Handle errors somehow.
+ Log.wtf(TAG, "Got device error " + error);
+ }
+
+ @Override
+ public void onActive(CameraDevice camera) {
+ mIdleDrainer.taskStarted();
+ mActive = true;
+
+ mStateListener.onActive(session);
+ }
+
+ @Override
+ public void onIdle(CameraDevice camera) {
+ boolean isAborting;
+ synchronized (session) {
+ isAborting = mAborting;
+ }
+
+ /*
+ * Check which states we transitioned through:
+ *
+ * (ACTIVE -> IDLE)
+ * (BUSY -> IDLE)
+ *
+ * Note that this is also legal:
+ * (ACTIVE -> BUSY -> IDLE)
+ *
+ * and mark those tasks as finished
+ */
+ if (mBusy && isAborting) {
+ mAbortDrainer.taskFinished();
+
+ synchronized (session) {
+ mAborting = false;
+ }
+ }
+
+ if (mActive) {
+ mIdleDrainer.taskFinished();
+ }
+
+ mBusy = false;
+ mActive = false;
+
+ mStateListener.onReady(session);
+ }
+
+ @Override
+ public void onBusy(CameraDevice camera) {
+ mBusy = true;
+
+ // TODO: Queue captures during abort instead of failing them
+ // since the app won't be able to distinguish the two actives
+ Log.w(TAG, "Device is now busy; do not submit new captures (TODO: allow this)");
+ mStateListener.onActive(session);
+ }
+
+ @Override
+ public void onUnconfigured(CameraDevice camera) {
+ synchronized (session) {
+ // Ignore #onUnconfigured before #close is called
+ if (mClosed) {
+ mUnconfigureDrainer.taskFinished();
+ }
+ }
+ }
+ };
+
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void checkLegalToCapture() {
+ if (mAborting) {
+ throw new IllegalStateException(
+ "Session is aborting captures; new captures are not permitted");
+ }
+ }
+
+ private void checkNotClosed() {
+ if (mClosed) {
+ throw new IllegalStateException(
+ "Session has been closed; further changes are illegal.");
+ }
+ }
+
+ /**
+ * Notify the session that a pending capture sequence has just been queued.
+ *
+ * <p>During a shutdown/close, the session waits until all pending sessions are finished
+ * before taking any further steps to shut down itself.</p>
+ *
+ * @see #finishPendingSequence
+ */
+ private int addPendingSequence(int sequenceId) {
+ mSequenceDrainer.taskStarted(sequenceId);
+ return sequenceId;
+ }
+
+ /**
+ * Notify the session that a pending capture sequence is now finished.
+ *
+ * <p>During a shutdown/close, once all pending sequences finish, it is safe to
+ * close the camera further by unconfiguring and then firing {@code onClosed}.</p>
+ */
+ private void finishPendingSequence(int sequenceId) {
+ mSequenceDrainer.taskFinished(sequenceId);
+ }
+
+ private class SequenceDrainListener implements TaskDrainer.DrainListener {
+ @Override
+ public void onDrained() {
+ /*
+ * No repeating request is set; and the capture queue has fully drained.
+ *
+ * If no captures were queued to begin with, and an abort was queued,
+ * it's still possible to get another BUSY before the last IDLE.
+ *
+ * If the camera is already "IDLE" and no aborts are pending,
+ * then the drain immediately finishes.
+ */
+ mAbortDrainer.beginDrain();
+ }
+ }
+
+ private class AbortDrainListener implements TaskDrainer.DrainListener {
+ @Override
+ public void onDrained() {
+ synchronized (CameraCaptureSessionImpl.this) {
+ /*
+ * Any queued aborts have now completed.
+ *
+ * It's now safe to wait to receive the final "IDLE" event, as the camera device
+ * will no longer again transition to "ACTIVE" by itself.
+ *
+ * If the camera is already "IDLE", then the drain immediately finishes.
+ */
+ mIdleDrainer.beginDrain();
+ }
+ }
+ }
+
+ private class IdleDrainListener implements TaskDrainer.DrainListener {
+ @Override
+ public void onDrained() {
+ synchronized (CameraCaptureSessionImpl.this) {
+ /*
+ * The device is now IDLE, and has settled. It will not transition to
+ * ACTIVE or BUSY again by itself.
+ *
+ * It's now safe to unconfigure the outputs and after it's done invoke #onClosed.
+ *
+ * This operation is idempotent; a session will not be closed twice.
+ */
+
+ // Fast path: A new capture session has replaced this one; don't unconfigure.
+ if (mSkipUnconfigure) {
+ mStateListener.onClosed(CameraCaptureSessionImpl.this);
+ return;
+ }
+
+ // Slow path: #close was called explicitly on this session; unconfigure first
+
+ try {
+ mUnconfigureDrainer.taskStarted();
+ mDeviceImpl.configureOutputs(null); // begin transition to unconfigured state
+ } catch (CameraAccessException e) {
+ // OK: do not throw checked exceptions.
+ Log.e(TAG, "Exception while configuring outputs: ", e);
+
+ // TODO: call onError instead of onClosed if this happens
+ }
+
+ mUnconfigureDrainer.beginDrain();
+ }
+ }
+ }
+
+ private class UnconfigureDrainListener implements TaskDrainer.DrainListener {
+ @Override
+ public void onDrained() {
+ synchronized (CameraCaptureSessionImpl.this) {
+ // The device has finished unconfiguring. It's now fully closed.
+ mStateListener.onClosed(CameraCaptureSessionImpl.this);
+ }
+ }
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDevice.java b/core/java/android/hardware/camera2/impl/CameraDevice.java
deleted file mode 100644
index 9a4c531..0000000
--- a/core/java/android/hardware/camera2/impl/CameraDevice.java
+++ /dev/null
@@ -1,984 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.impl;
-
-import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
-
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.ICameraDeviceCallbacks;
-import android.hardware.camera2.ICameraDeviceUser;
-import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.utils.CameraBinderDecorator;
-import android.hardware.camera2.utils.CameraRuntimeException;
-import android.hardware.camera2.utils.LongParcelable;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Surface;
-
-import java.util.AbstractMap.SimpleEntry;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.TreeSet;
-
-/**
- * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
- */
-public class CameraDevice implements android.hardware.camera2.CameraDevice {
-
- private final String TAG;
- private final boolean DEBUG;
-
- private static final int REQUEST_ID_NONE = -1;
-
- // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
- private ICameraDeviceUser mRemoteDevice;
-
- private final Object mLock = new Object();
- private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
-
- private final StateListener mDeviceListener;
- private final Handler mDeviceHandler;
-
- private boolean mIdle = true;
-
- private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
- new SparseArray<CaptureListenerHolder>();
-
- private int mRepeatingRequestId = REQUEST_ID_NONE;
- private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
- // Map stream IDs to Surfaces
- private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
-
- private final String mCameraId;
- private final CameraCharacteristics mCharacteristics;
-
- /**
- * A list tracking request and its expected last frame.
- * Updated when calling ICameraDeviceUser methods.
- */
- private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
- mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
-
- /**
- * An object tracking received frame numbers.
- * Updated when receiving callbacks from ICameraDeviceCallbacks.
- */
- private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
-
- // Runnables for all state transitions, except error, which needs the
- // error code argument
-
- private final Runnable mCallOnOpened = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- mDeviceListener.onOpened(CameraDevice.this);
- }
- }
- };
-
- private final Runnable mCallOnUnconfigured = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- mDeviceListener.onUnconfigured(CameraDevice.this);
- }
- }
- };
-
- private final Runnable mCallOnActive = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- mDeviceListener.onActive(CameraDevice.this);
- }
- }
- };
-
- private final Runnable mCallOnBusy = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- mDeviceListener.onBusy(CameraDevice.this);
- }
- }
- };
-
- private final Runnable mCallOnClosed = new Runnable() {
- @Override
- public void run() {
- mDeviceListener.onClosed(CameraDevice.this);
- }
- };
-
- private final Runnable mCallOnIdle = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- mDeviceListener.onIdle(CameraDevice.this);
- }
- }
- };
-
- private final Runnable mCallOnDisconnected = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- mDeviceListener.onDisconnected(CameraDevice.this);
- }
- }
- };
-
- public CameraDevice(String cameraId, StateListener listener, Handler handler,
- CameraCharacteristics characteristics) {
- if (cameraId == null || listener == null || handler == null) {
- throw new IllegalArgumentException("Null argument given");
- }
- mCameraId = cameraId;
- mDeviceListener = listener;
- mDeviceHandler = handler;
- mCharacteristics = characteristics;
-
- final int MAX_TAG_LEN = 23;
- String tag = String.format("CameraDevice-JV-%s", mCameraId);
- if (tag.length() > MAX_TAG_LEN) {
- tag = tag.substring(0, MAX_TAG_LEN);
- }
- TAG = tag;
-
- DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- }
-
- public CameraDeviceCallbacks getCallbacks() {
- return mCallbacks;
- }
-
- public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
- // TODO: Move from decorator to direct binder-mediated exceptions
- synchronized(mLock) {
- mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
-
- mDeviceHandler.post(mCallOnOpened);
- mDeviceHandler.post(mCallOnUnconfigured);
- }
- }
-
- @Override
- public String getId() {
- return mCameraId;
- }
-
- @Override
- public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
- // Treat a null input the same an empty list
- if (outputs == null) {
- outputs = new ArrayList<Surface>();
- }
- synchronized (mLock) {
- checkIfCameraClosed();
-
- HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
- List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
-
- // Determine which streams need to be created, which to be deleted
- for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
- int streamId = mConfiguredOutputs.keyAt(i);
- Surface s = mConfiguredOutputs.valueAt(i);
-
- if (!outputs.contains(s)) {
- deleteList.add(streamId);
- } else {
- addSet.remove(s); // Don't create a stream previously created
- }
- }
-
- mDeviceHandler.post(mCallOnBusy);
- stopRepeating();
-
- try {
- waitUntilIdle();
-
- mRemoteDevice.beginConfigure();
- // Delete all streams first (to free up HW resources)
- for (Integer streamId : deleteList) {
- mRemoteDevice.deleteStream(streamId);
- mConfiguredOutputs.delete(streamId);
- }
-
- // Add all new streams
- for (Surface s : addSet) {
- // TODO: remove width,height,format since we are ignoring
- // it.
- int streamId = mRemoteDevice.createStream(0, 0, 0, s);
- mConfiguredOutputs.put(streamId, s);
- }
-
- mRemoteDevice.endConfigure();
- } catch (CameraRuntimeException e) {
- if (e.getReason() == CAMERA_IN_USE) {
- throw new IllegalStateException("The camera is currently busy." +
- " You must wait until the previous operation completes.");
- }
-
- throw e.asChecked();
- } catch (RemoteException e) {
- // impossible
- return;
- }
-
- if (outputs.size() > 0) {
- mDeviceHandler.post(mCallOnIdle);
- } else {
- mDeviceHandler.post(mCallOnUnconfigured);
- }
- }
- }
-
- @Override
- public void createCaptureSession(List<Surface> outputs,
- CameraCaptureSession.StateListener listener, Handler handler)
- throws CameraAccessException {
- // TODO
- }
-
- @Override
- public CaptureRequest.Builder createCaptureRequest(int templateType)
- throws CameraAccessException {
- synchronized (mLock) {
- checkIfCameraClosed();
-
- CameraMetadataNative templatedRequest = new CameraMetadataNative();
-
- try {
- mRemoteDevice.createDefaultRequest(templateType, /* out */templatedRequest);
- } catch (CameraRuntimeException e) {
- throw e.asChecked();
- } catch (RemoteException e) {
- // impossible
- return null;
- }
-
- CaptureRequest.Builder builder =
- new CaptureRequest.Builder(templatedRequest);
-
- return builder;
- }
- }
-
- @Override
- public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
- throws CameraAccessException {
- if (DEBUG) {
- Log.d(TAG, "calling capture");
- }
- List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
- requestList.add(request);
- return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
- }
-
- @Override
- public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
- Handler handler) throws CameraAccessException {
- // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
- if (requests.isEmpty()) {
- Log.w(TAG, "Capture burst request list is empty, do nothing!");
- return -1;
- }
- return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
- }
-
- /**
- * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
- * starting and stopping repeating request and flushing.
- *
- * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
- * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
- * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
- * is added to the list mFrameNumberRequestPairs.</p>
- *
- * @param requestId the request ID of the current repeating request.
- *
- * @param lastFrameNumber last frame number returned from binder.
- */
- private void checkEarlyTriggerSequenceComplete(
- final int requestId, final long lastFrameNumber) {
- // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
- // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
- if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
- final CaptureListenerHolder holder;
- int index = mCaptureListenerMap.indexOfKey(requestId);
- holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
- if (holder != null) {
- mCaptureListenerMap.removeAt(index);
- if (DEBUG) {
- Log.v(TAG, String.format(
- "remove holder for requestId %d, "
- + "because lastFrame is %d.",
- requestId, lastFrameNumber));
- }
- }
-
- if (holder != null) {
- if (DEBUG) {
- Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
- + " request did not reach HAL");
- }
-
- Runnable resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- if (DEBUG) {
- Log.d(TAG, String.format(
- "early trigger sequence complete for request %d",
- requestId));
- }
- if (lastFrameNumber < Integer.MIN_VALUE
- || lastFrameNumber > Integer.MAX_VALUE) {
- throw new AssertionError(lastFrameNumber + " cannot be cast to int");
- }
- holder.getListener().onCaptureSequenceCompleted(
- CameraDevice.this,
- requestId,
- lastFrameNumber);
- }
- }
- };
- holder.getHandler().post(resultDispatch);
- } else {
- Log.w(TAG, String.format(
- "did not register listener to request %d",
- requestId));
- }
- } else {
- mFrameNumberRequestPairs.add(
- new SimpleEntry<Long, Integer>(lastFrameNumber,
- requestId));
- }
- }
-
- private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
- Handler handler, boolean repeating) throws CameraAccessException {
-
- // Need a valid handler, or current thread needs to have a looper, if
- // listener is valid
- if (listener != null) {
- handler = checkHandler(handler);
- }
-
- synchronized (mLock) {
- checkIfCameraClosed();
- int requestId;
-
- if (repeating) {
- stopRepeating();
- }
-
- LongParcelable lastFrameNumberRef = new LongParcelable();
- try {
- requestId = mRemoteDevice.submitRequestList(requestList, repeating,
- /*out*/lastFrameNumberRef);
- if (DEBUG) {
- Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
- }
- } catch (CameraRuntimeException e) {
- throw e.asChecked();
- } catch (RemoteException e) {
- // impossible
- return -1;
- }
-
- if (listener != null) {
- mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
- requestList, handler, repeating));
- } else {
- if (DEBUG) {
- Log.d(TAG, "Listen for request " + requestId + " is null");
- }
- }
-
- long lastFrameNumber = lastFrameNumberRef.getNumber();
-
- if (repeating) {
- if (mRepeatingRequestId != REQUEST_ID_NONE) {
- checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
- }
- mRepeatingRequestId = requestId;
- } else {
- mFrameNumberRequestPairs.add(
- new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
- }
-
- if (mIdle) {
- mDeviceHandler.post(mCallOnActive);
- }
- mIdle = false;
-
- return requestId;
- }
- }
-
- @Override
- public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
- Handler handler) throws CameraAccessException {
- List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
- requestList.add(request);
- return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
- }
-
- @Override
- public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
- Handler handler) throws CameraAccessException {
- // TODO: remove this. Throw IAE if the request is null or empty. Need to update API doc.
- if (requests.isEmpty()) {
- Log.w(TAG, "Set Repeating burst request list is empty, do nothing!");
- return -1;
- }
- return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
- }
-
- @Override
- public void stopRepeating() throws CameraAccessException {
-
- synchronized (mLock) {
- checkIfCameraClosed();
- if (mRepeatingRequestId != REQUEST_ID_NONE) {
-
- int requestId = mRepeatingRequestId;
- mRepeatingRequestId = REQUEST_ID_NONE;
-
- // Queue for deletion after in-flight requests finish
- if (mCaptureListenerMap.get(requestId) != null) {
- mRepeatingRequestIdDeletedList.add(requestId);
- }
-
- try {
- LongParcelable lastFrameNumberRef = new LongParcelable();
- mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
- long lastFrameNumber = lastFrameNumberRef.getNumber();
-
- checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
-
- } catch (CameraRuntimeException e) {
- throw e.asChecked();
- } catch (RemoteException e) {
- // impossible
- return;
- }
- }
- }
- }
-
- private void waitUntilIdle() throws CameraAccessException {
-
- synchronized (mLock) {
- checkIfCameraClosed();
- if (mRepeatingRequestId != REQUEST_ID_NONE) {
- throw new IllegalStateException("Active repeating request ongoing");
- }
-
- try {
- mRemoteDevice.waitUntilIdle();
- } catch (CameraRuntimeException e) {
- throw e.asChecked();
- } catch (RemoteException e) {
- // impossible
- return;
- }
-
- mRepeatingRequestId = REQUEST_ID_NONE;
- }
- }
-
- @Override
- public void flush() throws CameraAccessException {
- synchronized (mLock) {
- checkIfCameraClosed();
-
- mDeviceHandler.post(mCallOnBusy);
- try {
- LongParcelable lastFrameNumberRef = new LongParcelable();
- mRemoteDevice.flush(/*out*/lastFrameNumberRef);
- if (mRepeatingRequestId != REQUEST_ID_NONE) {
- long lastFrameNumber = lastFrameNumberRef.getNumber();
- checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
- mRepeatingRequestId = REQUEST_ID_NONE;
- }
- } catch (CameraRuntimeException e) {
- throw e.asChecked();
- } catch (RemoteException e) {
- // impossible
- return;
- }
- }
- }
-
- @Override
- public void close() {
- synchronized (mLock) {
-
- try {
- if (mRemoteDevice != null) {
- mRemoteDevice.disconnect();
- }
- } catch (CameraRuntimeException e) {
- Log.e(TAG, "Exception while closing: ", e.asChecked());
- } catch (RemoteException e) {
- // impossible
- }
-
- if (mRemoteDevice != null) {
- mDeviceHandler.post(mCallOnClosed);
- }
-
- mRemoteDevice = null;
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- close();
- }
- finally {
- super.finalize();
- }
- }
-
- static class CaptureListenerHolder {
-
- private final boolean mRepeating;
- private final CaptureListener mListener;
- private final List<CaptureRequest> mRequestList;
- private final Handler mHandler;
-
- CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
- Handler handler, boolean repeating) {
- if (listener == null || handler == null) {
- throw new UnsupportedOperationException(
- "Must have a valid handler and a valid listener");
- }
- mRepeating = repeating;
- mHandler = handler;
- mRequestList = new ArrayList<CaptureRequest>(requestList);
- mListener = listener;
- }
-
- public boolean isRepeating() {
- return mRepeating;
- }
-
- public CaptureListener getListener() {
- return mListener;
- }
-
- public CaptureRequest getRequest(int subsequenceId) {
- if (subsequenceId >= mRequestList.size()) {
- throw new IllegalArgumentException(
- String.format(
- "Requested subsequenceId %d is larger than request list size %d.",
- subsequenceId, mRequestList.size()));
- } else {
- if (subsequenceId < 0) {
- throw new IllegalArgumentException(String.format(
- "Requested subsequenceId %d is negative", subsequenceId));
- } else {
- return mRequestList.get(subsequenceId);
- }
- }
- }
-
- public CaptureRequest getRequest() {
- return getRequest(0);
- }
-
- public Handler getHandler() {
- return mHandler;
- }
-
- }
-
- /**
- * This class tracks the last frame number for submitted requests.
- */
- public class FrameNumberTracker {
-
- private long mCompletedFrameNumber = -1;
- private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
-
- private void update() {
- Iterator<Long> iter = mFutureErrorSet.iterator();
- while (iter.hasNext()) {
- long errorFrameNumber = iter.next();
- if (errorFrameNumber == mCompletedFrameNumber + 1) {
- mCompletedFrameNumber++;
- iter.remove();
- } else {
- break;
- }
- }
- }
-
- /**
- * This function is called every time when a result or an error is received.
- * @param frameNumber: the frame number corresponding to the result or error
- * @param isError: true if it is an error, false if it is not an error
- */
- public void updateTracker(long frameNumber, boolean isError) {
- if (isError) {
- mFutureErrorSet.add(frameNumber);
- } else {
- /**
- * HAL cannot send an OnResultReceived for frame N unless it knows for
- * sure that all frames prior to N have either errored out or completed.
- * So if the current frame is not an error, then all previous frames
- * should have arrived. The following line checks whether this holds.
- */
- if (frameNumber != mCompletedFrameNumber + 1) {
- Log.e(TAG, String.format(
- "result frame number %d comes out of order, should be %d + 1",
- frameNumber, mCompletedFrameNumber));
- }
- mCompletedFrameNumber++;
- }
- update();
- }
-
- public long getCompletedFrameNumber() {
- return mCompletedFrameNumber;
- }
-
- }
-
- private void checkAndFireSequenceComplete() {
- long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
- Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
- while (iter.hasNext()) {
- final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
- if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
-
- // remove request from mCaptureListenerMap
- final int requestId = frameNumberRequestPair.getValue();
- final CaptureListenerHolder holder;
- synchronized (mLock) {
- int index = mCaptureListenerMap.indexOfKey(requestId);
- holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
- : null;
- if (holder != null) {
- mCaptureListenerMap.removeAt(index);
- if (DEBUG) {
- Log.v(TAG, String.format(
- "remove holder for requestId %d, "
- + "because lastFrame %d is <= %d",
- requestId, frameNumberRequestPair.getKey(),
- completedFrameNumber));
- }
- }
- }
- iter.remove();
-
- // Call onCaptureSequenceCompleted
- if (holder != null) {
- Runnable resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()){
- if (DEBUG) {
- Log.d(TAG, String.format(
- "fire sequence complete for request %d",
- requestId));
- }
-
- long lastFrameNumber = frameNumberRequestPair.getKey();
- if (lastFrameNumber < Integer.MIN_VALUE
- || lastFrameNumber > Integer.MAX_VALUE) {
- throw new AssertionError(lastFrameNumber
- + " cannot be cast to int");
- }
- holder.getListener().onCaptureSequenceCompleted(
- CameraDevice.this,
- requestId,
- lastFrameNumber);
- }
- }
- };
- holder.getHandler().post(resultDispatch);
- }
-
- }
- }
- }
-
- public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
-
- //
- // Constants below need to be kept up-to-date with
- // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
- //
-
- //
- // Error codes for onCameraError
- //
-
- /**
- * Camera has been disconnected
- */
- static final int ERROR_CAMERA_DISCONNECTED = 0;
-
- /**
- * Camera has encountered a device-level error
- * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
- */
- static final int ERROR_CAMERA_DEVICE = 1;
-
- /**
- * Camera has encountered a service-level error
- * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
- */
- static final int ERROR_CAMERA_SERVICE = 2;
-
- @Override
- public IBinder asBinder() {
- return this;
- }
-
- @Override
- public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
- Runnable r = null;
- if (isClosed()) return;
-
- synchronized(mLock) {
- switch (errorCode) {
- case ERROR_CAMERA_DISCONNECTED:
- r = mCallOnDisconnected;
- break;
- default:
- Log.e(TAG, "Unknown error from camera device: " + errorCode);
- // no break
- case ERROR_CAMERA_DEVICE:
- case ERROR_CAMERA_SERVICE:
- r = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- mDeviceListener.onError(CameraDevice.this, errorCode);
- }
- }
- };
- break;
- }
- CameraDevice.this.mDeviceHandler.post(r);
- }
-
- // Fire onCaptureSequenceCompleted
- if (DEBUG) {
- Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
- }
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
- checkAndFireSequenceComplete();
-
- }
-
- @Override
- public void onCameraIdle() {
- if (isClosed()) return;
-
- if (DEBUG) {
- Log.d(TAG, "Camera now idle");
- }
- synchronized (mLock) {
- if (!CameraDevice.this.mIdle) {
- CameraDevice.this.mDeviceHandler.post(mCallOnIdle);
- }
- CameraDevice.this.mIdle = true;
- }
- }
-
- @Override
- public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
- int requestId = resultExtras.getRequestId();
- if (DEBUG) {
- Log.d(TAG, "Capture started for id " + requestId);
- }
- final CaptureListenerHolder holder;
-
- // Get the listener for this frame ID, if there is one
- synchronized (mLock) {
- holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
- }
-
- if (holder == null) {
- return;
- }
-
- if (isClosed()) return;
-
- // Dispatch capture start notice
- holder.getHandler().post(
- new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()) {
- holder.getListener().onCaptureStarted(
- CameraDevice.this,
- holder.getRequest(resultExtras.getSubsequenceId()),
- timestamp);
- }
- }
- });
- }
-
- @Override
- public void onResultReceived(CameraMetadataNative result,
- CaptureResultExtras resultExtras) throws RemoteException {
-
- int requestId = resultExtras.getRequestId();
- if (DEBUG) {
- Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
- + requestId);
- }
-
-
- // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
- result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
- getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
-
- final CaptureListenerHolder holder;
- synchronized (mLock) {
- holder = CameraDevice.this.mCaptureListenerMap.get(requestId);
- }
-
- Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
- boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
-
- // Update tracker (increment counter) when it's not a partial result.
- if (!quirkIsPartialResult) {
- mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
- }
-
- // Check if we have a listener for this
- if (holder == null) {
- if (DEBUG) {
- Log.d(TAG,
- "holder is null, early return at frame "
- + resultExtras.getFrameNumber());
- }
- return;
- }
-
- if (isClosed()) {
- if (DEBUG) {
- Log.d(TAG,
- "camera is closed, early return at frame "
- + resultExtras.getFrameNumber());
- }
- return;
- }
-
- final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
-
-
- Runnable resultDispatch = null;
-
- // Either send a partial result or the final capture completed result
- if (quirkIsPartialResult) {
- final CaptureResult resultAsCapture =
- new CaptureResult(result, request, requestId);
-
- // Partial result
- resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()){
- holder.getListener().onCapturePartial(
- CameraDevice.this,
- request,
- resultAsCapture);
- }
- }
- };
- } else {
- final TotalCaptureResult resultAsCapture =
- new TotalCaptureResult(result, request, requestId);
-
- // Final capture result
- resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDevice.this.isClosed()){
- holder.getListener().onCaptureCompleted(
- CameraDevice.this,
- request,
- resultAsCapture);
- }
- }
- };
- }
-
- holder.getHandler().post(resultDispatch);
-
- // Fire onCaptureSequenceCompleted
- if (!quirkIsPartialResult) {
- checkAndFireSequenceComplete();
- }
- }
-
- }
-
- /**
- * Default handler management. If handler is null, get the current thread's
- * Looper to create a Handler with. If no looper exists, throw exception.
- */
- private Handler checkHandler(Handler handler) {
- if (handler == null) {
- Looper looper = Looper.myLooper();
- if (looper == null) {
- throw new IllegalArgumentException(
- "No handler given, and current thread has no looper!");
- }
- handler = new Handler(looper);
- }
- return handler;
- }
-
- private void checkIfCameraClosed() {
- if (mRemoteDevice == null) {
- throw new IllegalStateException("CameraDevice was already closed");
- }
- }
-
- private boolean isClosed() {
- synchronized(mLock) {
- return (mRemoteDevice == null);
- }
- }
-
- private CameraCharacteristics getCharacteristics() {
- return mCharacteristics;
- }
-}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
new file mode 100644
index 0000000..d9f3af4
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -0,0 +1,1113 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.ICameraDeviceCallbacks;
+import android.hardware.camera2.ICameraDeviceUser;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.utils.CameraBinderDecorator;
+import android.hardware.camera2.utils.CameraRuntimeException;
+import android.hardware.camera2.utils.LongParcelable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Surface;
+
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
+
+/**
+ * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
+ */
+public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice {
+
+ private final String TAG;
+ private final boolean DEBUG;
+
+ private static final int REQUEST_ID_NONE = -1;
+
+ // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
+ private ICameraDeviceUser mRemoteDevice;
+
+ private final Object mLock = new Object();
+ private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
+
+ private final StateListener mDeviceListener;
+ private volatile StateListener mSessionStateListener;
+ private final Handler mDeviceHandler;
+
+ private boolean mInError = false;
+ private boolean mIdle = true;
+
+ /** map request IDs to listener/request data */
+ private final SparseArray<CaptureListenerHolder> mCaptureListenerMap =
+ new SparseArray<CaptureListenerHolder>();
+
+ private int mRepeatingRequestId = REQUEST_ID_NONE;
+ private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
+ // Map stream IDs to Surfaces
+ private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
+
+ private final String mCameraId;
+ private final CameraCharacteristics mCharacteristics;
+
+ /**
+ * A list tracking request and its expected last frame.
+ * Updated when calling ICameraDeviceUser methods.
+ */
+ private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
+ mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
+
+ /**
+ * An object tracking received frame numbers.
+ * Updated when receiving callbacks from ICameraDeviceCallbacks.
+ */
+ private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
+
+ private CameraCaptureSessionImpl mCurrentSession;
+
+ // Runnables for all state transitions, except error, which needs the
+ // error code argument
+
+ private final Runnable mCallOnOpened = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ StateListener sessionListener = mSessionStateListener;
+ if (sessionListener != null) {
+ sessionListener.onOpened(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onOpened(CameraDeviceImpl.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnUnconfigured = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ StateListener sessionListener = mSessionStateListener;
+ if (sessionListener != null) {
+ sessionListener.onUnconfigured(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onUnconfigured(CameraDeviceImpl.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnActive = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ StateListener sessionListener = mSessionStateListener;
+ if (sessionListener != null) {
+ sessionListener.onActive(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onActive(CameraDeviceImpl.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnBusy = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ StateListener sessionListener = mSessionStateListener;
+ if (sessionListener != null) {
+ sessionListener.onBusy(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onBusy(CameraDeviceImpl.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnClosed = new Runnable() {
+ @Override
+ public void run() {
+ StateListener sessionListener = mSessionStateListener;
+ if (sessionListener != null) {
+ sessionListener.onClosed(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onClosed(CameraDeviceImpl.this);
+ }
+ };
+
+ private final Runnable mCallOnIdle = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ StateListener sessionListener = mSessionStateListener;
+ if (sessionListener != null) {
+ sessionListener.onIdle(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onIdle(CameraDeviceImpl.this);
+ }
+ }
+ };
+
+ private final Runnable mCallOnDisconnected = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ StateListener sessionListener = mSessionStateListener;
+ if (sessionListener != null) {
+ sessionListener.onDisconnected(CameraDeviceImpl.this);
+ }
+ mDeviceListener.onDisconnected(CameraDeviceImpl.this);
+ }
+ }
+ };
+
+ public CameraDeviceImpl(String cameraId, StateListener listener, Handler handler,
+ CameraCharacteristics characteristics) {
+ if (cameraId == null || listener == null || handler == null) {
+ throw new IllegalArgumentException("Null argument given");
+ }
+ mCameraId = cameraId;
+ mDeviceListener = listener;
+ mDeviceHandler = handler;
+ mCharacteristics = characteristics;
+
+ final int MAX_TAG_LEN = 23;
+ String tag = String.format("CameraDevice-JV-%s", mCameraId);
+ if (tag.length() > MAX_TAG_LEN) {
+ tag = tag.substring(0, MAX_TAG_LEN);
+ }
+ TAG = tag;
+ DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ }
+
+ public CameraDeviceCallbacks getCallbacks() {
+ return mCallbacks;
+ }
+
+ public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
+ // TODO: Move from decorator to direct binder-mediated exceptions
+ synchronized(mLock) {
+ // If setRemoteFailure already called, do nothing
+ if (mInError) return;
+
+ mRemoteDevice = CameraBinderDecorator.newInstance(remoteDevice);
+
+ mDeviceHandler.post(mCallOnOpened);
+ mDeviceHandler.post(mCallOnUnconfigured);
+ }
+ }
+
+ /**
+ * Call to indicate failed connection to a remote camera device.
+ *
+ * <p>This places the camera device in the error state and informs the listener.
+ * Use in place of setRemoteDevice() when startup fails.</p>
+ */
+ public void setRemoteFailure(final CameraRuntimeException failure) {
+ int failureCode = StateListener.ERROR_CAMERA_DEVICE;
+ boolean failureIsError = true;
+
+ switch (failure.getReason()) {
+ case CameraAccessException.CAMERA_IN_USE:
+ failureCode = StateListener.ERROR_CAMERA_IN_USE;
+ break;
+ case CameraAccessException.MAX_CAMERAS_IN_USE:
+ failureCode = StateListener.ERROR_MAX_CAMERAS_IN_USE;
+ break;
+ case CameraAccessException.CAMERA_DISABLED:
+ failureCode = StateListener.ERROR_CAMERA_DISABLED;
+ break;
+ case CameraAccessException.CAMERA_DISCONNECTED:
+ failureIsError = false;
+ break;
+ case CameraAccessException.CAMERA_ERROR:
+ failureCode = StateListener.ERROR_CAMERA_DEVICE;
+ break;
+ default:
+ Log.wtf(TAG, "Unknown failure in opening camera device: " + failure.getReason());
+ break;
+ }
+ final int code = failureCode;
+ final boolean isError = failureIsError;
+ synchronized (mLock) {
+ mInError = true;
+ mDeviceHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (isError) {
+ mDeviceListener.onError(CameraDeviceImpl.this, code);
+ } else {
+ mDeviceListener.onDisconnected(CameraDeviceImpl.this);
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public String getId() {
+ return mCameraId;
+ }
+
+ @Override
+ public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
+ // Treat a null input the same an empty list
+ if (outputs == null) {
+ outputs = new ArrayList<Surface>();
+ }
+ synchronized (mLock) {
+ checkIfCameraClosedOrInError();
+
+ HashSet<Surface> addSet = new HashSet<Surface>(outputs); // Streams to create
+ List<Integer> deleteList = new ArrayList<Integer>(); // Streams to delete
+
+ // Determine which streams need to be created, which to be deleted
+ for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
+ int streamId = mConfiguredOutputs.keyAt(i);
+ Surface s = mConfiguredOutputs.valueAt(i);
+
+ if (!outputs.contains(s)) {
+ deleteList.add(streamId);
+ } else {
+ addSet.remove(s); // Don't create a stream previously created
+ }
+ }
+
+ mDeviceHandler.post(mCallOnBusy);
+ stopRepeating();
+
+ try {
+ waitUntilIdle();
+
+ mRemoteDevice.beginConfigure();
+ // Delete all streams first (to free up HW resources)
+ for (Integer streamId : deleteList) {
+ mRemoteDevice.deleteStream(streamId);
+ mConfiguredOutputs.delete(streamId);
+ }
+
+ // Add all new streams
+ for (Surface s : addSet) {
+ // TODO: remove width,height,format since we are ignoring
+ // it.
+ int streamId = mRemoteDevice.createStream(0, 0, 0, s);
+ mConfiguredOutputs.put(streamId, s);
+ }
+
+ mRemoteDevice.endConfigure();
+ } catch (CameraRuntimeException e) {
+ if (e.getReason() == CAMERA_IN_USE) {
+ throw new IllegalStateException("The camera is currently busy." +
+ " You must wait until the previous operation completes.");
+ }
+
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+
+ if (outputs.size() > 0) {
+ mDeviceHandler.post(mCallOnIdle);
+ } else {
+ mDeviceHandler.post(mCallOnUnconfigured);
+ }
+ }
+ }
+
+ @Override
+ public void createCaptureSession(List<Surface> outputs,
+ CameraCaptureSession.StateListener listener, Handler handler)
+ throws CameraAccessException {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Log.d(TAG, "createCaptureSession");
+ }
+
+ checkIfCameraClosedOrInError();
+
+ // TODO: we must be in UNCONFIGURED mode to begin with, or using another session
+
+ // TODO: dont block for this
+ boolean configureSuccess = true;
+ CameraAccessException pendingException = null;
+ try {
+ configureOutputs(outputs); // and then block until IDLE
+ } catch (CameraAccessException e) {
+ configureSuccess = false;
+ pendingException = e;
+ if (DEBUG) {
+ Log.v(TAG, "createCaptureSession - failed with exception ", e);
+ }
+ }
+
+ // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
+ CameraCaptureSessionImpl newSession =
+ new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler,
+ configureSuccess);
+
+ if (mCurrentSession != null) {
+ mCurrentSession.replaceSessionClose(newSession);
+ }
+
+ // TODO: wait until current session closes, then create the new session
+ mCurrentSession = newSession;
+
+ if (pendingException != null) {
+ throw pendingException;
+ }
+
+ mSessionStateListener = mCurrentSession.getDeviceStateListener();
+ }
+ }
+
+ @Override
+ public CaptureRequest.Builder createCaptureRequest(int templateType)
+ throws CameraAccessException {
+ synchronized (mLock) {
+ checkIfCameraClosedOrInError();
+
+ CameraMetadataNative templatedRequest = new CameraMetadataNative();
+
+ try {
+ mRemoteDevice.createDefaultRequest(templateType, /*out*/templatedRequest);
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return null;
+ }
+
+ CaptureRequest.Builder builder =
+ new CaptureRequest.Builder(templatedRequest);
+
+ return builder;
+ }
+ }
+
+ @Override
+ public int capture(CaptureRequest request, CaptureListener listener, Handler handler)
+ throws CameraAccessException {
+ if (DEBUG) {
+ Log.d(TAG, "calling capture");
+ }
+ List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+ requestList.add(request);
+ return submitCaptureRequest(requestList, listener, handler, /*streaming*/false);
+ }
+
+ @Override
+ public int captureBurst(List<CaptureRequest> requests, CaptureListener listener,
+ Handler handler) throws CameraAccessException {
+ if (requests == null || requests.isEmpty()) {
+ throw new IllegalArgumentException("At least one request must be given");
+ }
+ return submitCaptureRequest(requests, listener, handler, /*streaming*/false);
+ }
+
+ /**
+ * This method checks lastFrameNumber returned from ICameraDeviceUser methods for
+ * starting and stopping repeating request and flushing.
+ *
+ * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
+ * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
+ * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
+ * is added to the list mFrameNumberRequestPairs.</p>
+ *
+ * @param requestId the request ID of the current repeating request.
+ *
+ * @param lastFrameNumber last frame number returned from binder.
+ */
+ private void checkEarlyTriggerSequenceComplete(
+ final int requestId, final long lastFrameNumber) {
+ // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
+ // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
+ if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
+ final CaptureListenerHolder holder;
+ int index = mCaptureListenerMap.indexOfKey(requestId);
+ holder = (index >= 0) ? mCaptureListenerMap.valueAt(index) : null;
+ if (holder != null) {
+ mCaptureListenerMap.removeAt(index);
+ if (DEBUG) {
+ Log.v(TAG, String.format(
+ "remove holder for requestId %d, "
+ + "because lastFrame is %d.",
+ requestId, lastFrameNumber));
+ }
+ }
+
+ if (holder != null) {
+ if (DEBUG) {
+ Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
+ + " request did not reach HAL");
+ }
+
+ Runnable resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "early trigger sequence complete for request %d",
+ requestId));
+ }
+ if (lastFrameNumber < Integer.MIN_VALUE
+ || lastFrameNumber > Integer.MAX_VALUE) {
+ throw new AssertionError(lastFrameNumber + " cannot be cast to int");
+ }
+ holder.getListener().onCaptureSequenceCompleted(
+ CameraDeviceImpl.this,
+ requestId,
+ lastFrameNumber);
+ }
+ }
+ };
+ holder.getHandler().post(resultDispatch);
+ } else {
+ Log.w(TAG, String.format(
+ "did not register listener to request %d",
+ requestId));
+ }
+ } else {
+ mFrameNumberRequestPairs.add(
+ new SimpleEntry<Long, Integer>(lastFrameNumber,
+ requestId));
+ }
+ }
+
+ private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureListener listener,
+ Handler handler, boolean repeating) throws CameraAccessException {
+
+ // Need a valid handler, or current thread needs to have a looper, if
+ // listener is valid
+ if (listener != null) {
+ handler = checkHandler(handler);
+ }
+
+ synchronized (mLock) {
+ checkIfCameraClosedOrInError();
+ int requestId;
+
+ if (repeating) {
+ stopRepeating();
+ }
+
+ LongParcelable lastFrameNumberRef = new LongParcelable();
+ try {
+ requestId = mRemoteDevice.submitRequestList(requestList, repeating,
+ /*out*/lastFrameNumberRef);
+ if (DEBUG) {
+ Log.v(TAG, "last frame number " + lastFrameNumberRef.getNumber());
+ }
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return -1;
+ }
+
+ if (listener != null) {
+ mCaptureListenerMap.put(requestId, new CaptureListenerHolder(listener,
+ requestList, handler, repeating));
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Listen for request " + requestId + " is null");
+ }
+ }
+
+ long lastFrameNumber = lastFrameNumberRef.getNumber();
+
+ if (repeating) {
+ if (mRepeatingRequestId != REQUEST_ID_NONE) {
+ checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
+ }
+ mRepeatingRequestId = requestId;
+ } else {
+ mFrameNumberRequestPairs.add(
+ new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
+ }
+
+ if (mIdle) {
+ mDeviceHandler.post(mCallOnActive);
+ }
+ mIdle = false;
+
+ return requestId;
+ }
+ }
+
+ @Override
+ public int setRepeatingRequest(CaptureRequest request, CaptureListener listener,
+ Handler handler) throws CameraAccessException {
+ List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+ requestList.add(request);
+ return submitCaptureRequest(requestList, listener, handler, /*streaming*/true);
+ }
+
+ @Override
+ public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener,
+ Handler handler) throws CameraAccessException {
+ if (requests == null || requests.isEmpty()) {
+ throw new IllegalArgumentException("At least one request must be given");
+ }
+ return submitCaptureRequest(requests, listener, handler, /*streaming*/true);
+ }
+
+ @Override
+ public void stopRepeating() throws CameraAccessException {
+
+ synchronized (mLock) {
+ checkIfCameraClosedOrInError();
+ if (mRepeatingRequestId != REQUEST_ID_NONE) {
+
+ int requestId = mRepeatingRequestId;
+ mRepeatingRequestId = REQUEST_ID_NONE;
+
+ // Queue for deletion after in-flight requests finish
+ if (mCaptureListenerMap.get(requestId) != null) {
+ mRepeatingRequestIdDeletedList.add(requestId);
+ }
+
+ try {
+ LongParcelable lastFrameNumberRef = new LongParcelable();
+ mRemoteDevice.cancelRequest(requestId, /*out*/lastFrameNumberRef);
+ long lastFrameNumber = lastFrameNumberRef.getNumber();
+
+ checkEarlyTriggerSequenceComplete(requestId, lastFrameNumber);
+
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+ }
+ }
+ }
+
+ private void waitUntilIdle() throws CameraAccessException {
+
+ synchronized (mLock) {
+ checkIfCameraClosedOrInError();
+ if (mRepeatingRequestId != REQUEST_ID_NONE) {
+ throw new IllegalStateException("Active repeating request ongoing");
+ }
+
+ try {
+ mRemoteDevice.waitUntilIdle();
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+
+ mRepeatingRequestId = REQUEST_ID_NONE;
+ }
+ }
+
+ @Override
+ public void flush() throws CameraAccessException {
+ synchronized (mLock) {
+ checkIfCameraClosedOrInError();
+
+ mDeviceHandler.post(mCallOnBusy);
+ try {
+ LongParcelable lastFrameNumberRef = new LongParcelable();
+ mRemoteDevice.flush(/*out*/lastFrameNumberRef);
+ if (mRepeatingRequestId != REQUEST_ID_NONE) {
+ long lastFrameNumber = lastFrameNumberRef.getNumber();
+ checkEarlyTriggerSequenceComplete(mRepeatingRequestId, lastFrameNumber);
+ mRepeatingRequestId = REQUEST_ID_NONE;
+ }
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ synchronized (mLock) {
+
+ try {
+ if (mRemoteDevice != null) {
+ mRemoteDevice.disconnect();
+ }
+ } catch (CameraRuntimeException e) {
+ Log.e(TAG, "Exception while closing: ", e.asChecked());
+ } catch (RemoteException e) {
+ // impossible
+ }
+
+ // Only want to fire the onClosed callback once;
+ // either a normal close where the remote device is valid
+ // or a close after a startup error (no remote device but in error state)
+ if (mRemoteDevice != null || mInError) {
+ mDeviceHandler.post(mCallOnClosed);
+ }
+
+ mRemoteDevice = null;
+ mInError = false;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ }
+ finally {
+ super.finalize();
+ }
+ }
+
+ static class CaptureListenerHolder {
+
+ private final boolean mRepeating;
+ private final CaptureListener mListener;
+ private final List<CaptureRequest> mRequestList;
+ private final Handler mHandler;
+
+ CaptureListenerHolder(CaptureListener listener, List<CaptureRequest> requestList,
+ Handler handler, boolean repeating) {
+ if (listener == null || handler == null) {
+ throw new UnsupportedOperationException(
+ "Must have a valid handler and a valid listener");
+ }
+ mRepeating = repeating;
+ mHandler = handler;
+ mRequestList = new ArrayList<CaptureRequest>(requestList);
+ mListener = listener;
+ }
+
+ public boolean isRepeating() {
+ return mRepeating;
+ }
+
+ public CaptureListener getListener() {
+ return mListener;
+ }
+
+ public CaptureRequest getRequest(int subsequenceId) {
+ if (subsequenceId >= mRequestList.size()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Requested subsequenceId %d is larger than request list size %d.",
+ subsequenceId, mRequestList.size()));
+ } else {
+ if (subsequenceId < 0) {
+ throw new IllegalArgumentException(String.format(
+ "Requested subsequenceId %d is negative", subsequenceId));
+ } else {
+ return mRequestList.get(subsequenceId);
+ }
+ }
+ }
+
+ public CaptureRequest getRequest() {
+ return getRequest(0);
+ }
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ }
+
+ /**
+ * This class tracks the last frame number for submitted requests.
+ */
+ public class FrameNumberTracker {
+
+ private long mCompletedFrameNumber = -1;
+ private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
+
+ private void update() {
+ Iterator<Long> iter = mFutureErrorSet.iterator();
+ while (iter.hasNext()) {
+ long errorFrameNumber = iter.next();
+ if (errorFrameNumber == mCompletedFrameNumber + 1) {
+ mCompletedFrameNumber++;
+ iter.remove();
+ } else {
+ break;
+ }
+ }
+ }
+
+ /**
+ * This function is called every time when a result or an error is received.
+ * @param frameNumber: the frame number corresponding to the result or error
+ * @param isError: true if it is an error, false if it is not an error
+ */
+ public void updateTracker(long frameNumber, boolean isError) {
+ if (isError) {
+ mFutureErrorSet.add(frameNumber);
+ } else {
+ /**
+ * HAL cannot send an OnResultReceived for frame N unless it knows for
+ * sure that all frames prior to N have either errored out or completed.
+ * So if the current frame is not an error, then all previous frames
+ * should have arrived. The following line checks whether this holds.
+ */
+ if (frameNumber != mCompletedFrameNumber + 1) {
+ Log.e(TAG, String.format(
+ "result frame number %d comes out of order, should be %d + 1",
+ frameNumber, mCompletedFrameNumber));
+ }
+ mCompletedFrameNumber++;
+ }
+ update();
+ }
+
+ public long getCompletedFrameNumber() {
+ return mCompletedFrameNumber;
+ }
+
+ }
+
+ private void checkAndFireSequenceComplete() {
+ long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
+ Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
+ while (iter.hasNext()) {
+ final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
+ if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
+
+ // remove request from mCaptureListenerMap
+ final int requestId = frameNumberRequestPair.getValue();
+ final CaptureListenerHolder holder;
+ synchronized (mLock) {
+ int index = mCaptureListenerMap.indexOfKey(requestId);
+ holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
+ : null;
+ if (holder != null) {
+ mCaptureListenerMap.removeAt(index);
+ if (DEBUG) {
+ Log.v(TAG, String.format(
+ "remove holder for requestId %d, "
+ + "because lastFrame %d is <= %d",
+ requestId, frameNumberRequestPair.getKey(),
+ completedFrameNumber));
+ }
+ }
+ }
+ iter.remove();
+
+ // Call onCaptureSequenceCompleted
+ if (holder != null) {
+ Runnable resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "fire sequence complete for request %d",
+ requestId));
+ }
+
+ long lastFrameNumber = frameNumberRequestPair.getKey();
+ if (lastFrameNumber < Integer.MIN_VALUE
+ || lastFrameNumber > Integer.MAX_VALUE) {
+ throw new AssertionError(lastFrameNumber
+ + " cannot be cast to int");
+ }
+ holder.getListener().onCaptureSequenceCompleted(
+ CameraDeviceImpl.this,
+ requestId,
+ lastFrameNumber);
+ }
+ }
+ };
+ holder.getHandler().post(resultDispatch);
+ }
+
+ }
+ }
+ }
+
+ public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
+
+ //
+ // Constants below need to be kept up-to-date with
+ // frameworks/av/include/camera/camera2/ICameraDeviceCallbacks.h
+ //
+
+ //
+ // Error codes for onCameraError
+ //
+
+ /**
+ * Camera has been disconnected
+ */
+ static final int ERROR_CAMERA_DISCONNECTED = 0;
+
+ /**
+ * Camera has encountered a device-level error
+ * Matches CameraDevice.StateListener#ERROR_CAMERA_DEVICE
+ */
+ static final int ERROR_CAMERA_DEVICE = 1;
+
+ /**
+ * Camera has encountered a service-level error
+ * Matches CameraDevice.StateListener#ERROR_CAMERA_SERVICE
+ */
+ static final int ERROR_CAMERA_SERVICE = 2;
+
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ @Override
+ public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
+ Runnable r = null;
+ if (isClosed()) return;
+
+ synchronized(mLock) {
+ mInError = true;
+ switch (errorCode) {
+ case ERROR_CAMERA_DISCONNECTED:
+ r = mCallOnDisconnected;
+ break;
+ default:
+ Log.e(TAG, "Unknown error from camera device: " + errorCode);
+ // no break
+ case ERROR_CAMERA_DEVICE:
+ case ERROR_CAMERA_SERVICE:
+ r = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ mDeviceListener.onError(CameraDeviceImpl.this, errorCode);
+ }
+ }
+ };
+ break;
+ }
+ CameraDeviceImpl.this.mDeviceHandler.post(r);
+ }
+
+ // Fire onCaptureSequenceCompleted
+ if (DEBUG) {
+ Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+ }
+ mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
+ checkAndFireSequenceComplete();
+
+ }
+
+ @Override
+ public void onCameraIdle() {
+ if (isClosed()) return;
+
+ if (DEBUG) {
+ Log.d(TAG, "Camera now idle");
+ }
+ synchronized (mLock) {
+ if (!CameraDeviceImpl.this.mIdle) {
+ CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
+ }
+ CameraDeviceImpl.this.mIdle = true;
+ }
+ }
+
+ @Override
+ public void onCaptureStarted(final CaptureResultExtras resultExtras, final long timestamp) {
+ int requestId = resultExtras.getRequestId();
+ if (DEBUG) {
+ Log.d(TAG, "Capture started for id " + requestId);
+ }
+ final CaptureListenerHolder holder;
+
+ // Get the listener for this frame ID, if there is one
+ synchronized (mLock) {
+ holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
+ }
+
+ if (holder == null) {
+ return;
+ }
+
+ if (isClosed()) return;
+
+ // Dispatch capture start notice
+ holder.getHandler().post(
+ new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()) {
+ holder.getListener().onCaptureStarted(
+ CameraDeviceImpl.this,
+ holder.getRequest(resultExtras.getSubsequenceId()),
+ timestamp);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onResultReceived(CameraMetadataNative result,
+ CaptureResultExtras resultExtras) throws RemoteException {
+
+ int requestId = resultExtras.getRequestId();
+ if (DEBUG) {
+ Log.v(TAG, "Received result frame " + resultExtras.getFrameNumber() + " for id "
+ + requestId);
+ }
+
+
+ // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
+ result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
+ getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+
+ final CaptureListenerHolder holder;
+ synchronized (mLock) {
+ holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
+ }
+
+ Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
+ boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
+
+ // Update tracker (increment counter) when it's not a partial result.
+ if (!quirkIsPartialResult) {
+ mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
+ }
+
+ // Check if we have a listener for this
+ if (holder == null) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "holder is null, early return at frame "
+ + resultExtras.getFrameNumber());
+ }
+ return;
+ }
+
+ if (isClosed()) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "camera is closed, early return at frame "
+ + resultExtras.getFrameNumber());
+ }
+ return;
+ }
+
+ final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
+
+
+ Runnable resultDispatch = null;
+
+ // Either send a partial result or the final capture completed result
+ if (quirkIsPartialResult) {
+ final CaptureResult resultAsCapture =
+ new CaptureResult(result, request, requestId);
+
+ // Partial result
+ resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getListener().onCapturePartial(
+ CameraDeviceImpl.this,
+ request,
+ resultAsCapture);
+ }
+ }
+ };
+ } else {
+ final TotalCaptureResult resultAsCapture =
+ new TotalCaptureResult(result, request, requestId);
+
+ // Final capture result
+ resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ holder.getListener().onCaptureCompleted(
+ CameraDeviceImpl.this,
+ request,
+ resultAsCapture);
+ }
+ }
+ };
+ }
+
+ holder.getHandler().post(resultDispatch);
+
+ // Fire onCaptureSequenceCompleted
+ if (!quirkIsPartialResult) {
+ checkAndFireSequenceComplete();
+ }
+ }
+
+ }
+
+ /**
+ * Default handler management.
+ *
+ * <p>
+ * If handler is null, get the current thread's
+ * Looper to create a Handler with. If no looper exists, throw {@code IllegalArgumentException}.
+ * </p>
+ */
+ static Handler checkHandler(Handler handler) {
+ if (handler == null) {
+ Looper looper = Looper.myLooper();
+ if (looper == null) {
+ throw new IllegalArgumentException(
+ "No handler given, and current thread has no looper!");
+ }
+ handler = new Handler(looper);
+ }
+ return handler;
+ }
+
+ private void checkIfCameraClosedOrInError() throws CameraAccessException {
+ if (mInError) {
+ throw new CameraAccessException(CameraAccessException.CAMERA_ERROR,
+ "The camera device has encountered a serious error");
+ }
+ if (mRemoteDevice == null) {
+ throw new IllegalStateException("CameraDevice was already closed");
+ }
+ }
+
+ private boolean isClosed() {
+ synchronized(mLock) {
+ return (mRemoteDevice == null);
+ }
+ }
+
+ private CameraCharacteristics getCharacteristics() {
+ return mCharacteristics;
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/ListenerProxies.java b/core/java/android/hardware/camera2/impl/ListenerProxies.java
new file mode 100644
index 0000000..04c43e3
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/ListenerProxies.java
@@ -0,0 +1,168 @@
+package android.hardware.camera2.impl;
+
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.dispatch.Dispatchable;
+import android.hardware.camera2.dispatch.MethodNameInvoker;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Proxy out invocations to the camera2 API listeners into a {@link Dispatchable}.
+ *
+ * <p>Since abstract classes do not support Java's dynamic {@code Proxy}, we have to
+ * to use our own proxy mechanism.</p>
+ */
+public class ListenerProxies {
+
+ // TODO: replace with codegen
+
+ public static class DeviceStateListenerProxy extends CameraDevice.StateListener {
+ private final MethodNameInvoker<CameraDevice.StateListener> mProxy;
+
+ public DeviceStateListenerProxy(
+ Dispatchable<CameraDevice.StateListener> dispatchTarget) {
+ dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
+ mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDevice.StateListener.class);
+ }
+
+ @Override
+ public void onOpened(CameraDevice camera) {
+ mProxy.invoke("onOpened", camera);
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ mProxy.invoke("onDisconnected", camera);
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ mProxy.invoke("onError", camera, error);
+ }
+
+ @Override
+ public void onUnconfigured(CameraDevice camera) {
+ mProxy.invoke("onUnconfigured", camera);
+ }
+
+ @Override
+ public void onActive(CameraDevice camera) {
+ mProxy.invoke("onActive", camera);
+ }
+
+ @Override
+ public void onBusy(CameraDevice camera) {
+ mProxy.invoke("onBusy", camera);
+ }
+
+ @Override
+ public void onClosed(CameraDevice camera) {
+ mProxy.invoke("onClosed", camera);
+ }
+
+ @Override
+ public void onIdle(CameraDevice camera) {
+ mProxy.invoke("onIdle", camera);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public static class DeviceCaptureListenerProxy extends CameraDevice.CaptureListener {
+ private final MethodNameInvoker<CameraDevice.CaptureListener> mProxy;
+
+ public DeviceCaptureListenerProxy(
+ Dispatchable<CameraDevice.CaptureListener> dispatchTarget) {
+ dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
+ mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDevice.CaptureListener.class);
+ }
+
+ @Override
+ public void onCaptureStarted(CameraDevice camera,
+ CaptureRequest request, long timestamp) {
+ mProxy.invoke("onCaptureStarted", camera, request, timestamp);
+ }
+
+ @Override
+ public void onCapturePartial(CameraDevice camera,
+ CaptureRequest request, CaptureResult result) {
+ mProxy.invoke("onCapturePartial", camera, request, result);
+ }
+
+ @Override
+ public void onCaptureProgressed(CameraDevice camera,
+ CaptureRequest request, CaptureResult partialResult) {
+ mProxy.invoke("onCaptureProgressed", camera, request, partialResult);
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraDevice camera,
+ CaptureRequest request, TotalCaptureResult result) {
+ mProxy.invoke("onCaptureCompleted", camera, request, result);
+ }
+
+ @Override
+ public void onCaptureFailed(CameraDevice camera,
+ CaptureRequest request, CaptureFailure failure) {
+ mProxy.invoke("onCaptureFailed", camera, request, failure);
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(CameraDevice camera,
+ int sequenceId, long frameNumber) {
+ mProxy.invoke("onCaptureSequenceCompleted", camera, sequenceId, frameNumber);
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(CameraDevice camera,
+ int sequenceId) {
+ mProxy.invoke("onCaptureSequenceAborted", camera, sequenceId);
+ }
+ }
+
+ public static class SessionStateListenerProxy
+ extends CameraCaptureSession.StateListener {
+ private final MethodNameInvoker<CameraCaptureSession.StateListener> mProxy;
+
+ public SessionStateListenerProxy(
+ Dispatchable<CameraCaptureSession.StateListener> dispatchTarget) {
+ dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null");
+ mProxy = new MethodNameInvoker<>(dispatchTarget,
+ CameraCaptureSession.StateListener.class);
+ }
+
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ mProxy.invoke("onConfigured", session);
+ }
+
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ mProxy.invoke("onConfigureFailed", session);
+ }
+
+ @Override
+ public void onReady(CameraCaptureSession session) {
+ mProxy.invoke("onReady", session);
+ }
+
+ @Override
+ public void onActive(CameraCaptureSession session) {
+ mProxy.invoke("onActive", session);
+ }
+
+ @Override
+ public void onClosed(CameraCaptureSession session) {
+ mProxy.invoke("onClosed", session);
+ }
+ }
+
+ private ListenerProxies() {
+ throw new AssertionError();
+ }
+}
diff --git a/core/java/android/hardware/camera2/package.html b/core/java/android/hardware/camera2/package.html
index 9f6c2a9..ef0d7bd 100644
--- a/core/java/android/hardware/camera2/package.html
+++ b/core/java/android/hardware/camera2/package.html
@@ -34,49 +34,71 @@
CameraDevices} provide a set of static property information that
describes the hardware device and the available settings and output
parameters for the device. This information is provided through the
-{@link android.hardware.camera2.CameraCharacteristics} object.</p>
+{@link android.hardware.camera2.CameraCharacteristics} object, and is
+available through {@link
+android.hardware.camera2.CameraManager#getCameraCharacteristics}</p>
<p>To capture or stream images from a camera device, the application
-must first configure a set of output Surfaces for use with the camera
-device, with {@link
-android.hardware.camera2.CameraDevice#configureOutputs}. Each
-Surface has to be pre-configured with an appropriate size and format
-(if applicable) to match the sizes and formats available from the
-camera device. A target Surface can be obtained from a variety of
-classes, including {@link android.view.SurfaceView}, {@link
-android.graphics.SurfaceTexture} via {@link
-android.view.Surface#Surface(SurfaceTexture), {@link
-android.media.MediaCodec}, and {@link android.media.ImageReader}.
+must first create a {@link
+android.hardware.camera2.CameraCaptureSession camera capture session}
+with a set of output Surfaces for use with the camera device, with
+{@link
+android.hardware.camera2.CameraDevice#createCaptureSession}. Each
+Surface has to be pre-configured with an {@link
+android.hardware.camera2.params.StreamConfigurationMap appropriate
+size and format} (if applicable) to match the sizes and formats
+available from the camera device. A target Surface can be obtained
+from a variety of classes, including {@link android.view.SurfaceView},
+{@link android.graphics.SurfaceTexture} via
+{@link android.view.Surface#Surface(SurfaceTexture)},
+{@link android.media.MediaCodec}, {@link android.media.MediaRecorder},
+{@link android.renderscript.Allocation}, and {@link android.media.ImageReader}.
</p>
+<p>Generally, camera preview images are sent to {@link
+android.view.SurfaceView} or {@link android.view.TextureView} (via its
+{@link android.graphics.SurfaceTexture}). Capture of JPEG images or
+RAW buffers for {@link android.hardware.camera2.DngCreator} can be done
+with {@link android.media.ImageReader} with the
+{android.graphics.ImageFormat#JPEG} and
+{android.graphics.ImageFormat#RAW_SENSOR} formats. Application-driven
+processing of camera data in RenderScript, OpenGL ES, or directly in
+managed or native code is best done through {@link
+android.renderscript.Allocation} with a YUV {@link
+android.renderscript.Type}, {@link android.graphics.SurfaceTexture},
+and {@link android.media.ImageReader} with a
+{android.graphics.ImageFormat#YUV_420_888} format, respectively.</p>
+
<p>The application then needs to construct a {@link
android.hardware.camera2.CaptureRequest}, which defines all the
capture parameters needed by a camera device to capture a single
image. The request also lists which of the configured output Surfaces
should be used as targets for this capture. The CameraDevice has a
{@link android.hardware.camera2.CameraDevice#createCaptureRequest
-convenience factory method} for creating a request for a given use
-case which is optimized for the Android device the application is
-running on.</p>
+factory method} for creating a {@link
+android.hardware.camera2.CaptureRequest.Builder request builder} for a
+given use case, which is optimized for the Android device the
+application is running on.</p>
-<p>Once the request has been set up, it can be handed to the
-CameraDevice either for a one-shot {@link
-android.hardware.camera2.CameraDevice#capture} or for an endlessly
-{@link android.hardware.camera2.CameraDevice#setRepeatingRequest
-repeating} use. Both methods also accept a list of requests to use as
-a burst capture / repeating burst. Repeating requests have a lower
-priority than captures, so a request submitted
+<p>Once the request has been set up, it can be handed to the active
+capture session either for a one-shot {@link
+android.hardware.camera2.CameraCaptureSession#capture capture} or for
+an endlessly {@link
+android.hardware.camera2.CameraCaptureSession#setRepeatingRequest
+repeating} use. Both methods also have a variant that accepts a list
+of requests to use as a burst capture / repeating burst. Repeating
+requests have a lower priority than captures, so a request submitted
through <code>capture()</code> while there's a repeating request
-configured will be captured as soon as the current repeat (burst)
-capture completes.</p>
+configured will be captured before any new instances of the currently
+repeating (burst) capture will begin capture.</p>
<p>After processing a request, the camera device will produce a {@link
-android.hardware.camera2.CaptureResult} object, which contains
+android.hardware.camera2.TotalCaptureResult} object, which contains
information about the state of the camera device at time of capture,
and the final settings used. These may vary somewhat from the request,
if rounding or resolving contradictory parameters was necessary. The
camera device will also send a frame of image data into each of the
-output streams included in the request. These are produced
+output {@code Surfaces} included in the request. These are produced
asynchronously relative to the output CaptureResult, sometimes
substantially later.</p>
diff --git a/core/java/android/hardware/camera2/params/StreamConfiguration.java b/core/java/android/hardware/camera2/params/StreamConfiguration.java
index 1c6b6e9..a6fc10f 100644
--- a/core/java/android/hardware/camera2/params/StreamConfiguration.java
+++ b/core/java/android/hardware/camera2/params/StreamConfiguration.java
@@ -28,8 +28,9 @@
/**
* Immutable class to store the available stream
- * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to be used
- * when configuring streams with {@link CameraDevice#configureOutputs}.
+ * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to set up
+ * {@link android.view.Surface Surfaces} for creating a {@link CameraCaptureSession capture session}
+ * with {@link CameraDevice#createCaptureSession}.
* <!-- TODO: link to input stream configuration -->
*
* <p>This is the authoritative list for all input/output formats (and sizes respectively
@@ -124,7 +125,7 @@
*
* @return {@code true} if output configuration, {@code false} otherwise
*
- * @see CameraDevice#configureOutputs
+ * @see CameraDevice#createCaptureSession
*/
public boolean isOutput() {
return !mInput;
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 4cd6d15..3036425 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -34,8 +34,10 @@
/**
* Immutable class to store the available stream
- * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP configurations} to be used
- * when configuring streams with {@link CameraDevice#configureOutputs}.
+ * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP configurations} to set up
+ * {@link android.view.Surface Surfaces} for creating a
+ * {@link android.hardware.camera2.CameraCaptureSession capture session} with
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession}.
* <!-- TODO: link to input stream configuration -->
*
* <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively
@@ -56,7 +58,7 @@
* }</code></pre>
*
* @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
- * @see CameraDevice#configureOutputs
+ * @see CameraDevice#createCaptureSession
*/
public final class StreamConfigurationMap {
@@ -155,8 +157,8 @@
}
/**
- * Determine whether or not output streams can be
- * {@link CameraDevice#configureOutputs configured} with a particular user-defined format.
+ * Determine whether or not output surfaces with a particular user-defined format can be passed
+ * {@link CameraDevice#createCaptureSession createCaptureSession}.
*
* <p>This method determines that the output {@code format} is supported by the camera device;
* each output {@code surface} target may or may not itself support that {@code format}.
@@ -168,7 +170,7 @@
* @param format an image format from either {@link ImageFormat} or {@link PixelFormat}
* @return
* {@code true} iff using a {@code surface} with this {@code format} will be
- * supported with {@link CameraDevice#configureOutputs}
+ * supported with {@link CameraDevice#createCaptureSession}
*
* @throws IllegalArgumentException
* if the image format was not a defined named constant
@@ -176,7 +178,7 @@
*
* @see ImageFormat
* @see PixelFormat
- * @see CameraDevice#configureOutputs
+ * @see CameraDevice#createCaptureSession
*/
public boolean isOutputSupportedFor(int format) {
checkArgumentFormat(format);
@@ -210,7 +212,7 @@
*
* <p>Generally speaking this means that creating a {@link Surface} from that class <i>may</i>
* provide a producer endpoint that is suitable to be used with
- * {@link CameraDevice#configureOutputs}.</p>
+ * {@link CameraDevice#createCaptureSession}.</p>
*
* <p>Since not all of the above classes support output of all format and size combinations,
* the particular combination should be queried with {@link #isOutputSupportedFor(Surface)}.</p>
@@ -220,7 +222,7 @@
*
* @throws NullPointerException if {@code klass} was {@code null}
*
- * @see CameraDevice#configureOutputs
+ * @see CameraDevice#createCaptureSession
* @see #isOutputSupportedFor(Surface)
*/
public static <T> boolean isOutputSupportedFor(Class<T> klass) {
@@ -244,8 +246,8 @@
}
/**
- * Determine whether or not the {@code surface} in its current state is suitable to be
- * {@link CameraDevice#configureOutputs configured} as an output.
+ * Determine whether or not the {@code surface} in its current state is suitable to be included
+ * in a {@link CameraDevice#createCaptureSession capture session} as an output.
*
* <p>Not all surfaces are usable with the {@link CameraDevice}, and not all configurations
* of that {@code surface} are compatible. Some classes that provide the {@code surface} are
@@ -269,7 +271,7 @@
*
* @throws NullPointerException if {@code surface} was {@code null}
*
- * @see CameraDevice#configureOutputs
+ * @see CameraDevice#createCaptureSession
* @see #isOutputSupportedFor(Class)
*/
public boolean isOutputSupportedFor(Surface surface) {
diff --git a/core/java/android/hardware/camera2/params/TonemapCurve.java b/core/java/android/hardware/camera2/params/TonemapCurve.java
index 0fcffac..481d67a 100644
--- a/core/java/android/hardware/camera2/params/TonemapCurve.java
+++ b/core/java/android/hardware/camera2/params/TonemapCurve.java
@@ -78,7 +78,7 @@
/**
* Create a new immutable TonemapCurve instance.
*
- * <p>Values are stored as a contiguous {@code (Pin, Pout}) point.</p>
+ * <p>Values are stored as a contiguous array of {@code (Pin, Pout)} points.</p>
*
* <p>All parameters may have independent length but should have at most
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS} * {@value #POINT_SIZE} elements.</p>
@@ -88,15 +88,16 @@
*
* <p>This constructor copies the array contents and does not retain ownership of the array.</p>
*
- * @param elements An array of elements whose length is {@code CHANNEL_COUNT * rows * columns}
+ * @param red An array of elements whose length is divisible by {@value #POINT_SIZE}
+ * @param green An array of elements whose length is divisible by {@value #POINT_SIZE}
+ * @param blue An array of elements whose length is divisible by {@value #POINT_SIZE}
*
* @throws IllegalArgumentException
- * if the {@code elements} array length is invalid,
- * if any of the subelems are not finite
+ * if any of input array length is invalid,
+ * or if any of the elements in the array are not in the range of
+ * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}]
* @throws NullPointerException
- * if any of the parameters is {@code null}
- *
- * @hide
+ * if any of the parameters are {@code null}
*/
public TonemapCurve(float[] red, float[] green, float[] blue) {
// TODO: maxCurvePoints check?
diff --git a/core/java/android/hardware/camera2/utils/TaskDrainer.java b/core/java/android/hardware/camera2/utils/TaskDrainer.java
new file mode 100644
index 0000000..dc09f62
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/TaskDrainer.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.utils;
+
+import android.os.Handler;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Keep track of multiple concurrent tasks starting and finishing by their key;
+ * allow draining existing tasks and figuring out when all tasks have finished
+ * (and new ones won't begin).
+ *
+ * <p>The initial state is to allow all tasks to be started and finished. A task may only be started
+ * once, after which it must be finished before starting again. Likewise, finishing a task
+ * that hasn't been started is also not allowed.</p>
+ *
+ * <p>When draining begins, no more new tasks can be started. This guarantees that at some
+ * point when all the tasks are finished there will be no more collective new tasks,
+ * at which point the {@link DrainListener#onDrained} callback will be invoked.</p>
+ *
+ *
+ * @param <T>
+ * a type for the key that will represent tracked tasks;
+ * must implement {@code Object#equals}
+ */
+public class TaskDrainer<T> {
+ /**
+ * Fired asynchronously after draining has begun with {@link TaskDrainer#beginDrain}
+ * <em>and</em> all tasks that were started have finished.
+ */
+ public interface DrainListener {
+ /** All tasks have fully finished draining; there will be no more pending tasks. */
+ public void onDrained();
+ }
+
+ private static final String TAG = "TaskDrainer";
+ private final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private final Handler mHandler;
+ private final DrainListener mListener;
+ private final String mName;
+
+ /** Set of tasks which have been started but not yet finished with #taskFinished */
+ private final Set<T> mTaskSet = new HashSet<T>();
+ private final Object mLock = new Object();
+
+ private boolean mDraining = false;
+ private boolean mDrainFinished = false;
+
+ /**
+ * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
+ * via the {@code handler}.
+ *
+ * @param handler a non-{@code null} handler to use to post runnables to
+ * @param listener a non-{@code null} listener where {@code onDrained} will be called
+ */
+ public TaskDrainer(Handler handler, DrainListener listener) {
+ mHandler = checkNotNull(handler, "handler must not be null");
+ mListener = checkNotNull(listener, "listener must not be null");
+ mName = null;
+ }
+
+ /**
+ * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
+ * via the {@code handler}.
+ *
+ * @param handler a non-{@code null} handler to use to post runnables to
+ * @param listener a non-{@code null} listener where {@code onDrained} will be called
+ * @param name an optional name used for debug logging
+ */
+ public TaskDrainer(Handler handler, DrainListener listener, String name) {
+ // XX: Probably don't need a handler at all here
+ mHandler = checkNotNull(handler, "handler must not be null");
+ mListener = checkNotNull(listener, "listener must not be null");
+ mName = name;
+ }
+
+ /**
+ * Mark an asynchronous task as having started.
+ *
+ * <p>A task cannot be started more than once without first having finished. Once
+ * draining begins with {@link #beginDrain}, no new tasks can be started.</p>
+ *
+ * @param task a key to identify a task
+ *
+ * @see #taskFinished
+ * @see #beginDrain
+ *
+ * @throws IllegalStateException
+ * If attempting to start a task which is already started (and not finished),
+ * or if attempting to start a task after draining has begun.
+ */
+ public void taskStarted(T task) {
+ synchronized (mLock) {
+ if (VERBOSE) {
+ Log.v(TAG + "[" + mName + "]", "taskStarted " + task);
+ }
+
+ if (mDraining) {
+ throw new IllegalStateException("Can't start more tasks after draining has begun");
+ }
+
+ if (!mTaskSet.add(task)) {
+ throw new IllegalStateException("Task " + task + " was already started");
+ }
+ }
+ }
+
+
+ /**
+ * Mark an asynchronous task as having finished.
+ *
+ * <p>A task cannot be finished if it hasn't started. Once finished, a task
+ * cannot be finished again (unless it's started again).</p>
+ *
+ * @param task a key to identify a task
+ *
+ * @see #taskStarted
+ * @see #beginDrain
+ *
+ * @throws IllegalStateException
+ * If attempting to start a task which is already finished (and not re-started),
+ */
+ public void taskFinished(T task) {
+ synchronized (mLock) {
+ if (VERBOSE) {
+ Log.v(TAG + "[" + mName + "]", "taskFinished " + task);
+ }
+
+ if (!mTaskSet.remove(task)) {
+ throw new IllegalStateException("Task " + task + " was already finished");
+ }
+
+ // If this is the last finished task and draining has already begun, fire #onDrained
+ checkIfDrainFinished();
+ }
+ }
+
+ /**
+ * Do not allow any more tasks to be started; once all existing started tasks are finished,
+ * fire the {@link DrainListener#onDrained} callback asynchronously.
+ *
+ * <p>This operation is idempotent; calling it more than once has no effect.</p>
+ */
+ public void beginDrain() {
+ synchronized (mLock) {
+ if (!mDraining) {
+ if (VERBOSE) {
+ Log.v(TAG + "[" + mName + "]", "beginDrain started");
+ }
+
+ mDraining = true;
+
+ // If all tasks that had started had already finished by now, fire #onDrained
+ checkIfDrainFinished();
+ } else {
+ if (VERBOSE) {
+ Log.v(TAG + "[" + mName + "]", "beginDrain ignored");
+ }
+ }
+ }
+ }
+
+ private void checkIfDrainFinished() {
+ if (mTaskSet.isEmpty() && mDraining && !mDrainFinished) {
+ mDrainFinished = true;
+ postDrained();
+ }
+ }
+
+ private void postDrained() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (VERBOSE) {
+ Log.v(TAG + "[" + mName + "]", "onDrained");
+ }
+
+ mListener.onDrained();
+ }
+ });
+ }
+}
diff --git a/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
new file mode 100644
index 0000000..f6272c9
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/TaskSingleDrainer.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.camera2.utils;
+
+import android.hardware.camera2.utils.TaskDrainer.DrainListener;
+import android.os.Handler;
+
+/**
+ * Keep track of a single concurrent task starting and finishing;
+ * allow draining the existing task and figuring out when the task has finished
+ * (and won't restart).
+ *
+ * <p>The initial state is to allow all tasks to be started and finished. A task may only be started
+ * once, after which it must be finished before starting again. Likewise, finishing a task
+ * that hasn't been started is also not allowed.</p>
+ *
+ * <p>When draining begins, the task cannot be started again. This guarantees that at some
+ * point the task will be finished forever, at which point the {@link DrainListener#onDrained}
+ * callback will be invoked.</p>
+ */
+public class TaskSingleDrainer {
+
+ private final TaskDrainer<Object> mTaskDrainer;
+ private final Object mSingleTask = new Object();
+
+ /**
+ * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
+ * via the {@code handler}.
+ *
+ * @param handler a non-{@code null} handler to use to post runnables to
+ * @param listener a non-{@code null} listener where {@code onDrained} will be called
+ */
+ public TaskSingleDrainer(Handler handler, DrainListener listener) {
+ mTaskDrainer = new TaskDrainer<>(handler, listener);
+ }
+
+ /**
+ * Create a new task drainer; {@code onDrained} callbacks will be posted to the listener
+ * via the {@code handler}.
+ *
+ * @param handler a non-{@code null} handler to use to post runnables to
+ * @param listener a non-{@code null} listener where {@code onDrained} will be called
+ * @param name an optional name used for debug logging
+ */
+ public TaskSingleDrainer(Handler handler, DrainListener listener, String name) {
+ mTaskDrainer = new TaskDrainer<>(handler, listener, name);
+ }
+
+ /**
+ * Mark this asynchronous task as having started.
+ *
+ * <p>The task cannot be started more than once without first having finished. Once
+ * draining begins with {@link #beginDrain}, no new tasks can be started.</p>
+ *
+ * @see #taskFinished
+ * @see #beginDrain
+ *
+ * @throws IllegalStateException
+ * If attempting to start a task which is already started (and not finished),
+ * or if attempting to start a task after draining has begun.
+ */
+ public void taskStarted() {
+ mTaskDrainer.taskStarted(mSingleTask);
+ }
+
+ /**
+ * Do not allow any more task re-starts; once the existing task is finished,
+ * fire the {@link DrainListener#onDrained} callback asynchronously.
+ *
+ * <p>This operation is idempotent; calling it more than once has no effect.</p>
+ */
+ public void beginDrain() {
+ mTaskDrainer.beginDrain();
+ }
+
+ /**
+ * Mark this asynchronous task as having finished.
+ *
+ * <p>The task cannot be finished if it hasn't started. Once finished, a task
+ * cannot be finished again (unless it's started again).</p>
+ *
+ * @see #taskStarted
+ * @see #beginDrain
+ *
+ * @throws IllegalStateException
+ * If attempting to start a task which is already finished (and not re-started),
+ */
+ public void taskFinished() {
+ mTaskDrainer.taskFinished(mSingleTask);
+ }
+}
diff --git a/core/java/android/hardware/camera2/utils/UncheckedThrow.java b/core/java/android/hardware/camera2/utils/UncheckedThrow.java
index 8224fed..ffcb78b 100644
--- a/core/java/android/hardware/camera2/utils/UncheckedThrow.java
+++ b/core/java/android/hardware/camera2/utils/UncheckedThrow.java
@@ -33,8 +33,20 @@
UncheckedThrow.<RuntimeException>throwAnyImpl(e);
}
+ /**
+ * Throw any kind of throwable without needing it to be checked
+ * @param e any instance of a Throwable
+ */
+ public static void throwAnyException(Throwable e) {
+ /**
+ * Abuse type erasure by making the compiler think we are throwing RuntimeException,
+ * which is unchecked, but then inserting any exception in there.
+ */
+ UncheckedThrow.<RuntimeException>throwAnyImpl(e);
+ }
+
@SuppressWarnings("unchecked")
- private static<T extends Exception> void throwAnyImpl(Exception e) throws T {
+ private static<T extends Throwable> void throwAnyImpl(Throwable e) throws T {
throw (T) e;
}
}
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index 723eda1..8ad9463 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -16,9 +16,14 @@
package android.hardware.hdmi;
+import android.annotation.SystemApi;
+
/**
* Defines constants and utility methods related to HDMI-CEC protocol.
+ *
+ * @hide
*/
+@SystemApi
public final class HdmiCec {
/** TV device type. */
diff --git a/core/java/android/hardware/hdmi/HdmiCecClient.java b/core/java/android/hardware/hdmi/HdmiCecClient.java
deleted file mode 100644
index dcb3624..0000000
--- a/core/java/android/hardware/hdmi/HdmiCecClient.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.hdmi;
-
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import android.util.Log;
-
-/**
- * HdmiCecClient is used to control HDMI-CEC logical device instance in the system.
- * It is connected to actual hardware part via HdmiCecService. It provides with methods
- * to send CEC messages to other device on the bus, and listener that allows to receive
- * incoming messages to the device.
- */
-public final class HdmiCecClient {
- private static final String TAG = "HdmiCecClient";
-
- private final IHdmiCecService mService;
- private final IBinder mBinder;
-
- /**
- * Listener used by the client to get the incoming messages.
- */
- public static abstract class Listener {
- /**
- * Called when CEC message arrives. Override this method to receive the incoming
- * CEC messages from other device on the bus.
- *
- * @param message {@link HdmiCecMessage} object
- */
- public void onMessageReceived(HdmiCecMessage message) { }
-
- /**
- * Called when hotplug event occurs. Override this method to receive the events.
- *
- * @param connected true if the cable is connected; otherwise false.
- */
- public void onCableStatusChanged(boolean connected) { }
- }
-
- // Private constructor.
- private HdmiCecClient(IHdmiCecService service, IBinder b) {
- mService = service;
- mBinder = b;
- }
-
- // Factory method for HdmiCecClient.
- // Declared package-private. Accessed by HdmiCecManager only.
- static HdmiCecClient create(IHdmiCecService service, IBinder b) {
- return new HdmiCecClient(service, b);
- }
-
- /**
- * Send <Active Source> message.
- */
- public void sendActiveSource() {
- Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
- }
-
- /**
- * Send <Inactive Source> message.
- */
- public void sendInactiveSource() {
- Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
- }
-
- /**
- * Send <Text View On> message.
- */
- public void sendTextViewOn() {
- Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
- }
-
- /**
- * Send <Image View On> message.
- */
- public void sendImageViewOn() {
- Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
- }
-
- /**
- * Send <Give Device Power Status> message.
- *
- * @param address logical address of the device to send the message to, such as
- * {@link HdmiCec#ADDR_TV}.
- */
- public void sendGiveDevicePowerStatus(int address) {
- Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
- }
-
- /**
- * Returns true if the TV or attached display is powered on.
- * <p>
- * The result of this method is only meaningful on playback devices (where the device
- * type is {@link HdmiCec#DEVICE_PLAYBACK}).
- * </p>
- *
- * @return true if TV is on; otherwise false.
- */
- public boolean isTvOn() {
- Log.w(TAG, "In transition to HdmiControlManager. Will not work.");
- return true;
- }
-}
diff --git a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
index 9698445..fbfcca0 100644
--- a/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
+++ b/core/java/android/hardware/hdmi/HdmiCecDeviceInfo.java
@@ -16,6 +16,7 @@
package android.hardware.hdmi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,7 +24,10 @@
* A class to encapsulate device information for HDMI-CEC. This container
* include basic information such as logical address, physical address and
* device type, and additional information like vendor id and osd name.
+ *
+ * @hide
*/
+@SystemApi
public final class HdmiCecDeviceInfo implements Parcelable {
// Logical address, phsical address, device type, vendor id and display name
// are immutable value.
diff --git a/core/java/android/hardware/hdmi/HdmiCecManager.java b/core/java/android/hardware/hdmi/HdmiCecManager.java
deleted file mode 100644
index 03c46d8..0000000
--- a/core/java/android/hardware/hdmi/HdmiCecManager.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.hdmi;
-
-import android.os.IBinder;
-import android.os.RemoteException;
-
-/**
- * The HdmiCecManager class is used to provide an HdmiCecClient instance,
- * get various information on HDMI ports configuration. It is connected to actual hardware
- * via HdmiCecService.
- */
-public final class HdmiCecManager {
- private final IHdmiCecService mService;
-
- /**
- * @hide - hide this constructor because it has a parameter of type IHdmiCecService,
- * which is a system private class. The right way to create an instance of this class
- * is using the factory Context.getSystemService.
- */
- public HdmiCecManager(IHdmiCecService service) {
- mService = service;
- }
-
- /**
- * Provide the HdmiCecClient instance of the given type. It also registers the listener
- * for client to get the events coming to the device.
- *
- * @param type type of the HDMI-CEC logical device
- * @param listener listener to be called
- * @return {@link HdmiCecClient} instance. {@code null} on failure.
- */
- public HdmiCecClient getClient(int type, HdmiCecClient.Listener listener) {
- return HdmiCecClient.create(mService, null);
- }
-
- private IHdmiCecListener getListenerWrapper(final HdmiCecClient.Listener listener) {
- // TODO: The message/events are not yet forwarded to client since it is not clearly
- // defined as to how/who to handle them. Revisit it once the decision is
- // made on what messages will have to reach the clients, what will be
- // handled by service/manager.
- return new IHdmiCecListener.Stub() {
- @Override
- public void onMessageReceived(HdmiCecMessage message) {
- // Do nothing.
- }
-
- @Override
- public void onCableStatusChanged(boolean connected) {
- // Do nothing.
- }
- };
- }
-}
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
index 62fa279..ac16ad8 100644
--- a/core/java/android/hardware/hdmi/HdmiCecMessage.java
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -16,6 +16,7 @@
package android.hardware.hdmi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,7 +28,10 @@
* A class to encapsulate HDMI-CEC message used for the devices connected via
* HDMI cable to communicate with one another. A message is defined by its
* source and destination address, command (or opcode), and optional parameters.
+ *
+ * @hide
*/
+@SystemApi
public final class HdmiCecMessage implements Parcelable {
public static final byte[] EMPTY_PARAM = EmptyArray.BYTE;
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index 5b6e862..f15fa00 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -17,6 +17,7 @@
package android.hardware.hdmi;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.RemoteException;
/**
@@ -28,7 +29,10 @@
* {@link HdmiTvClient} object if the system is configured to host one. Android system
* can host more than one logical CEC devices. If multiple types are configured they
* all work as if they were independent logical devices running in the system.
+ *
+ * @hide
*/
+@SystemApi
public final class HdmiControlManager {
@Nullable private final IHdmiControlService mService;
diff --git a/core/java/android/hardware/hdmi/HdmiHotplugEvent.java b/core/java/android/hardware/hdmi/HdmiHotplugEvent.java
index 1462f83..7be4bc5 100644
--- a/core/java/android/hardware/hdmi/HdmiHotplugEvent.java
+++ b/core/java/android/hardware/hdmi/HdmiHotplugEvent.java
@@ -16,12 +16,16 @@
package android.hardware.hdmi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A class that describes the HDMI port hotplug event.
+ *
+ * @hide
*/
+@SystemApi
public final class HdmiHotplugEvent implements Parcelable {
private final int mPort;
diff --git a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
index f0bd237..2e49a38 100644
--- a/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
+++ b/core/java/android/hardware/hdmi/HdmiPlaybackClient.java
@@ -16,6 +16,7 @@
package android.hardware.hdmi;
+import android.annotation.SystemApi;
import android.os.RemoteException;
import android.util.Log;
@@ -25,7 +26,10 @@
* in the Android system which acts as a playback device such as set-top box.
* It provides with methods that control, get information from TV/Display device
* connected through HDMI bus.
+ *
+ * @hide
*/
+@SystemApi
public final class HdmiPlaybackClient {
private static final String TAG = "HdmiPlaybackClient";
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.aidl b/core/java/android/hardware/hdmi/HdmiPortInfo.aidl
new file mode 100644
index 0000000..157b5b3
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.hdmi;
+
+parcelable HdmiPortInfo;
diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java
new file mode 100644
index 0000000..7b25f8a
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.hdmi;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class to encapsulate HDMI port information. Contains the capability of the ports such as
+ * HDMI-CEC, MHL, ARC(Audio Return Channel), and physical address assigned to each port.
+ *
+ * @hide
+ */
+@SystemApi
+public final class HdmiPortInfo implements Parcelable {
+ /** HDMI port type: Input */
+ public static final int PORT_INPUT = 0;
+
+ /** HDMI port type: Output */
+ public static final int PORT_OUTPUT = 1;
+
+ private final int mId;
+ private final int mType;
+ private final int mAddress;
+ private final boolean mCecSupported;
+ private final boolean mArcSupported;
+ private final boolean mMhlSupported;
+
+ /**
+ * Constructor.
+ *
+ * @param id identifier assigned to each port. 1 for HDMI port 1
+ * @param type HDMI port input/output type
+ * @param address physical address of the port
+ * @param cec {@code true} if HDMI-CEC is supported on the port
+ * @param mhl {@code true} if MHL is supported on the port
+ * @param arc {@code true} if audio return channel is supported on the port
+ */
+ public HdmiPortInfo(int id, int type, int address, boolean cec, boolean mhl, boolean arc) {
+ mId = id;
+ mType = type;
+ mAddress = address;
+ mCecSupported = cec;
+ mArcSupported = arc;
+ mMhlSupported = mhl;
+ }
+
+ /**
+ * Returns the port id.
+ *
+ * @return port id
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the port type.
+ *
+ * @return port type
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the port address.
+ *
+ * @return port address
+ */
+ public int getAddress() {
+ return mAddress;
+ }
+
+ /**
+ * Returns {@code true} if the port supports HDMI-CEC signaling.
+ *
+ * @return {@code true} if the port supports HDMI-CEC signaling.
+ */
+ public boolean isCecSupported() {
+ return mCecSupported;
+ }
+
+ /**
+ * Returns {@code true} if the port supports MHL signaling.
+ *
+ * @return {@code true} if the port supports MHL signaling.
+ */
+ public boolean isMhlSupported() {
+ return mMhlSupported;
+ }
+
+ /**
+ * Returns {@code true} if the port supports audio return channel.
+ *
+ * @return {@code true} if the port supports audio return channel
+ */
+ public boolean isArcSupported() {
+ return mArcSupported;
+ }
+
+ /**
+ * Describe the kinds of special objects contained in this Parcelable's
+ * marshalled representation.
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+
+ /**
+ * A helper class to deserialize {@link HdmiPortInfo} for a parcel.
+ */
+ public static final Parcelable.Creator<HdmiPortInfo> CREATOR =
+ new Parcelable.Creator<HdmiPortInfo>() {
+ @Override
+ public HdmiPortInfo createFromParcel(Parcel source) {
+ int id = source.readInt();
+ int type = source.readInt();
+ int address = source.readInt();
+ boolean cec = (source.readInt() == 1);
+ boolean arc = (source.readInt() == 1);
+ boolean mhl = (source.readInt() == 1);
+ return new HdmiPortInfo(id, type, address, cec, arc, mhl);
+ }
+
+ @Override
+ public HdmiPortInfo[] newArray(int size) {
+ return new HdmiPortInfo[size];
+ }
+ };
+
+ /**
+ * Serialize this object into a {@link Parcel}.
+ *
+ * @param dest The Parcel in which the object should be written.
+ * @param flags Additional flags about how the object should be written.
+ * May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mId);
+ dest.writeInt(mType);
+ dest.writeInt(mAddress);
+ dest.writeInt(mCecSupported ? 1 : 0);
+ dest.writeInt(mArcSupported ? 1 : 0);
+ dest.writeInt(mMhlSupported ? 1 : 0);
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 73c72472..85af3d1 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -15,11 +15,18 @@
*/
package android.hardware.hdmi;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.util.Log;
+
/**
* HdmiTvClient represents HDMI-CEC logical device of type TV in the Android system
* which acts as TV/Display. It provides with methods that manage, interact with other
* devices on the CEC bus.
+ *
+ * @hide
*/
+@SystemApi
public final class HdmiTvClient {
private static final String TAG = "HdmiTvClient";
@@ -28,4 +35,46 @@
HdmiTvClient(IHdmiControlService service) {
mService = service;
}
+
+ // Factory method for HdmiTvClient.
+ // Declared package-private. Accessed by HdmiControlManager only.
+ static HdmiTvClient create(IHdmiControlService service) {
+ return new HdmiTvClient(service);
+ }
+
+ /**
+ * Callback interface used to get the result of {@link #deviceSelect}.
+ */
+ public interface SelectCallback {
+ /**
+ * Called when the operation is finished.
+ *
+ * @param result the result value of {@link #deviceSelect}
+ */
+ void onComplete(int result);
+ }
+
+ /**
+ * Select a CEC logical device to be a new active source.
+ *
+ * @param logicalAddress
+ * @param callback
+ */
+ public void deviceSelect(int logicalAddress, SelectCallback callback) {
+ // TODO: Replace SelectCallback with PartialResult.
+ try {
+ mService.deviceSelect(logicalAddress, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to select device: ", e);
+ }
+ }
+
+ private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) {
+ return new IHdmiControlCallback.Stub() {
+ @Override
+ public void onComplete(int result) {
+ callback.onComplete(result);
+ }
+ };
+ }
}
diff --git a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
deleted file mode 100644
index d281ce6..0000000
--- a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.hdmi;
-
-import android.hardware.hdmi.HdmiCecMessage;
-
-/**
- * Interface definition for HdmiCecService to do interprocess communcation.
- *
- * @hide
- */
-oneway interface IHdmiCecListener {
- void onMessageReceived(in HdmiCecMessage message);
- void onCableStatusChanged(in boolean connected);
-}
diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
deleted file mode 100644
index ecdd345..0000000
--- a/core/java/android/hardware/hdmi/IHdmiCecService.aidl
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.hdmi;
-
-import android.hardware.hdmi.HdmiCecMessage;
-import android.hardware.hdmi.IHdmiCecListener;
-import android.os.IBinder;
-
-/**
- * Binder interface that components running in the appplication process
- * will use to enable HDMI-CEC protocol exchange with other devices.
- *
- * @hide
- */
-interface IHdmiCecService {
- IBinder allocateLogicalDevice(int type, IHdmiCecListener listener);
- void removeServiceListener(IBinder b, IHdmiCecListener listener);
- void sendActiveSource(IBinder b);
- void sendInactiveSource(IBinder b);
- void sendImageViewOn(IBinder b);
- void sendTextViewOn(IBinder b);
- void sendGiveDevicePowerStatus(IBinder b, int address);
- boolean isTvOn(IBinder b);
- void sendMessage(IBinder b, in HdmiCecMessage message);
-}
-
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 8da38e1..8d7c638 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -33,4 +33,5 @@
void queryDisplayStatus(IHdmiControlCallback callback);
void addHotplugEventListener(IHdmiHotplugEventListener listener);
void removeHotplugEventListener(IHdmiHotplugEventListener listener);
+ void deviceSelect(int logicalAddress, IHdmiControlCallback callback);
}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
new file mode 100644
index 0000000..2d7af85
--- /dev/null
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+
+import java.util.ArrayList;
+import java.util.UUID;
+
+/**
+ * The SoundTrigger class provides access via JNI to the native service managing
+ * the sound trigger HAL.
+ *
+ * @hide
+ */
+public class SoundTrigger {
+
+ public static final int STATUS_OK = 0;
+ public static final int STATUS_ERROR = Integer.MIN_VALUE;
+ public static final int STATUS_PERMISSION_DENIED = -1;
+ public static final int STATUS_NO_INIT = -19;
+ public static final int STATUS_BAD_VALUE = -22;
+ public static final int STATUS_DEAD_OBJECT = -32;
+ public static final int STATUS_INVALID_OPERATION = -38;
+
+ /*****************************************************************************
+ * A ModuleProperties describes a given sound trigger hardware module
+ * managed by the native sound trigger service. Each module has a unique
+ * ID used to target any API call to this paricular module. Module
+ * properties are returned by listModules() method.
+ ****************************************************************************/
+ public static class ModuleProperties {
+ /** Unique module ID provided by the native service */
+ public final int id;
+
+ /** human readable voice detection engine implementor */
+ public final String implementor;
+
+ /** human readable voice detection engine description */
+ public final String description;
+
+ /** Unique voice engine Id (changes with each version) */
+ public final UUID uuid;
+
+ /** Voice detection engine version */
+ public final int version;
+
+ /** Maximum number of active sound models */
+ public final int maxSoundModels;
+
+ /** Maximum number of key phrases */
+ public final int maxKeyPhrases;
+
+ /** Maximum number of users per key phrase */
+ public final int maxUsers;
+
+ /** Supported recognition modes (bit field, RECOGNITION_MODE_VOICE_TRIGGER ...) */
+ public final int recognitionModes;
+
+ /** Supports seamless transition to capture mode after recognition */
+ public final boolean supportsCaptureTransition;
+
+ /** Maximum buffering capacity in ms if supportsCaptureTransition() is true */
+ public final int maxBufferMs;
+
+ /** Supports capture by other use cases while detection is active */
+ public final boolean supportsConcurrentCapture;
+
+ /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */
+ public final int powerConsumptionMw;
+
+ ModuleProperties(int id, String implementor, String description,
+ String uuid, int version, int maxSoundModels, int maxKeyPhrases,
+ int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
+ int maxBufferMs, boolean supportsConcurrentCapture,
+ int powerConsumptionMw) {
+ this.id = id;
+ this.implementor = implementor;
+ this.description = description;
+ this.uuid = UUID.fromString(uuid);
+ this.version = version;
+ this.maxSoundModels = maxSoundModels;
+ this.maxKeyPhrases = maxKeyPhrases;
+ this.maxUsers = maxUsers;
+ this.recognitionModes = recognitionModes;
+ this.supportsCaptureTransition = supportsCaptureTransition;
+ this.maxBufferMs = maxBufferMs;
+ this.supportsConcurrentCapture = supportsConcurrentCapture;
+ this.powerConsumptionMw = powerConsumptionMw;
+ }
+ }
+
+ /*****************************************************************************
+ * A SoundModel describes the attributes and contains the binary data used by the hardware
+ * implementation to detect a particular sound pattern.
+ * A specialized version {@link KeyPhraseSoundModel} is defined for key phrase
+ * sound models.
+ ****************************************************************************/
+ public static class SoundModel {
+ /** Undefined sound model type */
+ public static final int TYPE_UNKNOWN = -1;
+
+ /** Keyphrase sound model */
+ public static final int TYPE_KEYPHRASE = 0;
+
+ /** Sound model type (e.g. TYPE_KEYPHRASE); */
+ public final int type;
+
+ /** Opaque data. For use by vendor implementation and enrollment application */
+ public final byte[] data;
+
+ public SoundModel(int type, byte[] data) {
+ this.type = type;
+ this.data = data;
+ }
+ }
+
+ /*****************************************************************************
+ * A KeyPhrase describes a key phrase that can be detected by a
+ * {@link KeyPhraseSoundModel}
+ ****************************************************************************/
+ public static class KeyPhrase {
+ /** Recognition modes supported for this key phrase in the model */
+ public final int recognitionModes;
+
+ /** Locale of the keyphrase. JAVA Locale string e.g en_US */
+ public final String locale;
+
+ /** Key phrase text */
+ public final String text;
+
+ /** Number of users this key phrase has been trained for */
+ public final int numUsers;
+
+ public KeyPhrase(int recognitionModes, String locale, String text, int numUsers) {
+ this.recognitionModes = recognitionModes;
+ this.locale = locale;
+ this.text = text;
+ this.numUsers = numUsers;
+ }
+ }
+
+ /*****************************************************************************
+ * A KeyPhraseSoundModel is a specialized {@link SoundModel} for key phrases.
+ * It contains data needed by the hardware to detect a certain number of key phrases
+ * and the list of corresponding {@link KeyPhrase} descriptors.
+ ****************************************************************************/
+ public static class KeyPhraseSoundModel extends SoundModel {
+ /** Key phrases in this sound model */
+ public final KeyPhrase[] keyPhrases; // keyword phrases in model
+
+ public KeyPhraseSoundModel(byte[] data, KeyPhrase[] keyPhrases) {
+ super(TYPE_KEYPHRASE, data);
+ this.keyPhrases = keyPhrases;
+ }
+ }
+
+ /**
+ * Modes for key phrase recognition
+ */
+ /** Simple recognition of the key phrase */
+ public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
+ /** Trigger only if one user is identified */
+ public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
+ /** Trigger only if one user is authenticated */
+ public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
+
+ /**
+ * Status codes for {@link RecognitionEvent}
+ */
+ /** Recognition success */
+ public static final int RECOGNITION_STATUS_SUCCESS = 0;
+ /** Recognition aborted (e.g. capture preempted by anotehr use case */
+ public static final int RECOGNITION_STATUS_ABORT = 1;
+ /** Recognition failure */
+ public static final int RECOGNITION_STATUS_FAILURE = 2;
+
+ /**
+ * A RecognitionEvent is provided by the
+ * {@link StatusListener#onRecognition(RecognitionEvent)}
+ * callback upon recognition success or failure.
+ */
+ public static class RecognitionEvent {
+ /** Recognition status e.g {@link #RECOGNITION_STATUS_SUCCESS} */
+ public final int status;
+ /** Sound Model corresponding to this event callback */
+ public final int soundModelHandle;
+ /** True if it is possible to capture audio from this utterance buffered by the hardware */
+ public final boolean captureAvailable;
+ /** Audio session ID to be used when capturing the utterance with an AudioRecord
+ * if captureAvailable() is true. */
+ public final int captureSession;
+ /** Delay in ms between end of model detection and start of audio available for capture.
+ * A negative value is possible (e.g. if keyphrase is also available for capture) */
+ public final int captureDelayMs;
+ /** Opaque data for use by system applications who know about voice engine internals,
+ * typically during enrollment. */
+ public final byte[] data;
+
+ RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+ int captureSession, int captureDelayMs, byte[] data) {
+ this.status = status;
+ this.soundModelHandle = soundModelHandle;
+ this.captureAvailable = captureAvailable;
+ this.captureSession = captureSession;
+ this.captureDelayMs = captureDelayMs;
+ this.data = data;
+ }
+ }
+
+ /**
+ * Additional data conveyed by a {@link KeyPhraseRecognitionEvent}
+ * for a key phrase detection.
+ */
+ public static class KeyPhraseRecognitionExtra {
+ /** Confidence level for each user defined in the key phrase in the same order as
+ * users in the key phrase. The confidence level is expressed in percentage (0% -100%) */
+ public final int[] confidenceLevels;
+
+ /** Recognition modes matched for this event */
+ public final int recognitionModes;
+
+ KeyPhraseRecognitionExtra(int[] confidenceLevels, int recognitionModes) {
+ this.confidenceLevels = confidenceLevels;
+ this.recognitionModes = recognitionModes;
+ }
+ }
+
+ /**
+ * Specialized {@link RecognitionEvent} for a key phrase detection.
+ */
+ public static class KeyPhraseRecognitionEvent extends RecognitionEvent {
+ /** Indicates if the key phrase is present in the buffered audio available for capture */
+ public final KeyPhraseRecognitionExtra[] keyPhraseExtras;
+
+ /** Additional data available for each recognized key phrases in the model */
+ public final boolean keyPhraseInCapture;
+
+ KeyPhraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+ int captureSession, int captureDelayMs, byte[] data,
+ boolean keyPhraseInCapture, KeyPhraseRecognitionExtra[] keyPhraseExtras) {
+ super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, data);
+ this.keyPhraseInCapture = keyPhraseInCapture;
+ this.keyPhraseExtras = keyPhraseExtras;
+ }
+ }
+
+ /**
+ * Returns a list of descriptors for all harware modules loaded.
+ * @param modules A ModuleProperties array where the list will be returned.
+ * @return - {@link #STATUS_OK} in case of success
+ * - {@link #STATUS_ERROR} in case of unspecified error
+ * - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
+ * - {@link #STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link #STATUS_BAD_VALUE} if modules is null
+ * - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
+ */
+ public static native int listModules(ArrayList <ModuleProperties> modules);
+
+ /**
+ * Get an interface on a hardware module to control sound models and recognition on
+ * this module.
+ * @param moduleId Sound module system identifier {@link ModuleProperties#id}. mandatory.
+ * @param listener {@link StatusListener} interface. Mandatory.
+ * @param handler the Handler that will receive the callabcks. Can be null if default handler
+ * is OK.
+ * @return a valid sound module in case of success or null in case of error.
+ */
+ public static SoundTriggerModule attachModule(int moduleId,
+ StatusListener listener,
+ Handler handler) {
+ if (listener == null) {
+ return null;
+ }
+ SoundTriggerModule module = new SoundTriggerModule(moduleId, listener, handler);
+ return module;
+ }
+
+ /**
+ * Interface provided by the client application when attaching to a {@link SoundTriggerModule}
+ * to received recognition and error notifications.
+ */
+ public static interface StatusListener {
+ /**
+ * Called when recognition succeeds of fails
+ */
+ public abstract void onRecognition(RecognitionEvent event);
+
+ /**
+ * Called when the sound trigger native service dies
+ */
+ public abstract void onServiceDied();
+ }
+}
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
new file mode 100644
index 0000000..776f85d
--- /dev/null
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.soundtrigger;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import java.lang.ref.WeakReference;
+import java.util.UUID;
+
+/**
+ * The SoundTriggerModule provides APIs to control sound models and sound detection
+ * on a given sound trigger hardware module.
+ *
+ * @hide
+ */
+public class SoundTriggerModule {
+ private long mNativeContext;
+
+ private int mId;
+ private NativeEventHandlerDelegate mEventHandlerDelegate;
+
+ private static final int EVENT_RECOGNITION = 1;
+ private static final int EVENT_SERVICE_DIED = 2;
+
+ SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) {
+ mId = moduleId;
+ mEventHandlerDelegate = new NativeEventHandlerDelegate(listener, handler);
+ native_setup(new WeakReference<SoundTriggerModule>(this));
+ }
+ private native void native_setup(Object module_this);
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+ private native void native_finalize();
+
+ /**
+ * Detach from this module. The {@link SoundTrigger.StatusListener} callback will not be called
+ * anymore and associated resources will be released.
+ * */
+ public native void detach();
+
+ /**
+ * Load a {@link SoundTrigger.SoundModel} to the hardware. A sound model must be loaded in
+ * order to start listening to a key phrase in this model.
+ * @param model The sound model to load.
+ * @param soundModelHandle an array of int where the sound model handle will be returned.
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
+ * system permission
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} if parameters are invalid
+ * - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
+ * service fails
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
+ */
+ public native int loadSoundModel(SoundTrigger.SoundModel model, int[] soundModelHandle);
+
+ /**
+ * Unload a {@link SoundTrigger.SoundModel} and abort any pendiong recognition
+ * @param soundModelHandle The sound model handle
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
+ * system permission
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} if the sound model handle is invalid
+ * - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
+ * service fails
+ */
+ public native int unloadSoundModel(int soundModelHandle);
+
+ /**
+ * Start listening to all key phrases in a {@link SoundTrigger.SoundModel}.
+ * Recognition must be restarted after each callback (success or failure) received on
+ * the {@link SoundTrigger.StatusListener}.
+ * @param soundModelHandle The sound model handle to start listening to
+ * @param data Opaque data for use by the implementation for this recognition
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
+ * system permission
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} if the sound model handle is invalid
+ * - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
+ * service fails
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
+ */
+ public native int startRecognition(int soundModelHandle, byte[] data);
+
+ /**
+ * Stop listening to all key phrases in a {@link SoundTrigger.SoundModel}
+ * @param soundModelHandle The sound model handle to stop listening to
+ * @return - {@link SoundTrigger#STATUS_OK} in case of success
+ * - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
+ * - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
+ * system permission
+ * - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
+ * - {@link SoundTrigger#STATUS_BAD_VALUE} if the sound model handle is invalid
+ * - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
+ * service fails
+ * - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
+ */
+ public native int stopRecognition(int soundModelHandle);
+
+ private class NativeEventHandlerDelegate {
+ private final Handler mHandler;
+
+ NativeEventHandlerDelegate(final SoundTrigger.StatusListener listener,
+ Handler handler) {
+ // find the looper for our new event handler
+ Looper looper;
+ if (handler != null) {
+ looper = handler.getLooper();
+ } else {
+ looper = Looper.myLooper();
+ if (looper == null) {
+ looper = Looper.getMainLooper();
+ }
+ }
+
+ // construct the event handler with this looper
+ if (looper != null) {
+ // implement the event handler delegate
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch(msg.what) {
+ case EVENT_RECOGNITION:
+ if (listener != null) {
+ listener.onRecognition(
+ (SoundTrigger.RecognitionEvent)msg.obj);
+ }
+ break;
+ case EVENT_SERVICE_DIED:
+ if (listener != null) {
+ listener.onServiceDied();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
+ }
+
+ Handler handler() {
+ return mHandler;
+ }
+ }
+
+ @SuppressWarnings("unused")
+ private static void postEventFromNative(Object module_ref,
+ int what, int arg1, int arg2, Object obj) {
+ SoundTriggerModule module = (SoundTriggerModule)((WeakReference)module_ref).get();
+ if (module == null) {
+ return;
+ }
+
+ NativeEventHandlerDelegate delegate = module.mEventHandlerDelegate;
+ if (delegate != null) {
+ Handler handler = delegate.handler();
+ if (handler != null) {
+ Message m = handler.obtainMessage(what, arg1, arg2, obj);
+ handler.sendMessage(m);
+ }
+ }
+ }
+}
+
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a48a388..ba31243 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -22,6 +22,7 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
@@ -428,6 +429,11 @@
*/
public final static int INVALID_NET_ID = 0;
+ /**
+ * @hide
+ */
+ public final static int REQUEST_ID_UNSET = 0;
+
private final IConnectivityManager mService;
private final String mPackageName;
@@ -862,14 +868,49 @@
return -1;
}
- NetworkRequest request = removeRequestForFeature(netCap);
- if (request != null) {
+ NetworkCallback networkCallback = removeRequestForFeature(netCap);
+ if (networkCallback != null) {
Log.d(TAG, "stopUsingNetworkFeature for " + networkType + ", " + feature);
- releaseNetworkRequest(request);
+ unregisterNetworkCallback(networkCallback);
}
return 1;
}
+ /**
+ * Removes the NET_CAPABILITY_NOT_RESTRICTED capability from the given
+ * NetworkCapabilities object if all the capabilities it provides are
+ * typically provided by restricted networks.
+ *
+ * TODO: consider:
+ * - Moving to NetworkCapabilities
+ * - Renaming it to guessRestrictedCapability and make it set the
+ * restricted capability bit in addition to clearing it.
+ * @hide
+ */
+ public static void maybeMarkCapabilitiesRestricted(NetworkCapabilities nc) {
+ for (int capability : nc.getCapabilities()) {
+ switch (capability) {
+ case NetworkCapabilities.NET_CAPABILITY_CBS:
+ case NetworkCapabilities.NET_CAPABILITY_DUN:
+ case NetworkCapabilities.NET_CAPABILITY_EIMS:
+ case NetworkCapabilities.NET_CAPABILITY_FOTA:
+ case NetworkCapabilities.NET_CAPABILITY_IA:
+ case NetworkCapabilities.NET_CAPABILITY_IMS:
+ case NetworkCapabilities.NET_CAPABILITY_RCS:
+ case NetworkCapabilities.NET_CAPABILITY_XCAP:
+ case NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED: //there by default
+ continue;
+ default:
+ // At least one capability usually provided by unrestricted
+ // networks. Conclude that this network is unrestricted.
+ return;
+ }
+ }
+ // All the capabilities are typically provided by restricted networks.
+ // Conclude that this network is restricted.
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ }
+
private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) {
if (networkType == TYPE_MOBILE) {
int cap = -1;
@@ -891,14 +932,15 @@
return null;
}
NetworkCapabilities netCap = new NetworkCapabilities();
- netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- netCap.addNetworkCapability(cap);
+ netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addCapability(cap);
+ maybeMarkCapabilitiesRestricted(netCap);
return netCap;
} else if (networkType == TYPE_WIFI) {
if ("p2p".equals(feature)) {
NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
- netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P);
+ netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P);
+ maybeMarkCapabilitiesRestricted(netCap);
return netCap;
}
}
@@ -940,18 +982,18 @@
int expireSequenceNumber;
Network currentNetwork;
int delay = -1;
- NetworkCallbackListener networkCallbackListener = new NetworkCallbackListener() {
+ NetworkCallback networkCallback = new NetworkCallback() {
@Override
- public void onAvailable(NetworkRequest request, Network network) {
+ public void onAvailable(Network network) {
currentNetwork = network;
Log.d(TAG, "startUsingNetworkFeature got Network:" + network);
- network.bindProcessForHostResolution();
+ setProcessDefaultNetworkForHostResolution(network);
}
@Override
- public void onLost(NetworkRequest request, Network network) {
+ public void onLost(Network network) {
if (network.equals(currentNetwork)) {
currentNetwork = null;
- network.unbindProcessForHostResolution();
+ setProcessDefaultNetworkForHostResolution(null);
}
Log.d(TAG, "startUsingNetworkFeature lost Network:" + network);
}
@@ -982,7 +1024,7 @@
if (l == null) return;
ourSeqNum = l.expireSequenceNumber;
if (l.expireSequenceNumber == sequenceNum) {
- releaseNetworkRequest(l.networkRequest);
+ unregisterNetworkCallback(l.networkCallback);
sLegacyRequests.remove(netCap);
}
}
@@ -999,7 +1041,7 @@
l.networkCapabilities = netCap;
l.delay = delay;
l.expireSequenceNumber = 0;
- l.networkRequest = sendRequestForNetwork(netCap, l.networkCallbackListener, 0,
+ l.networkRequest = sendRequestForNetwork(netCap, l.networkCallback, 0,
REQUEST, type);
if (l.networkRequest == null) return null;
sLegacyRequests.put(netCap, l);
@@ -1015,11 +1057,11 @@
}
}
- private NetworkRequest removeRequestForFeature(NetworkCapabilities netCap) {
+ private NetworkCallback removeRequestForFeature(NetworkCapabilities netCap) {
synchronized (sLegacyRequests) {
LegacyRequest l = sLegacyRequests.remove(netCap);
if (l == null) return null;
- return l.networkRequest;
+ return l.networkCallback;
}
}
@@ -1035,7 +1077,7 @@
* @return {@code true} on success, {@code false} on failure
*
* @deprecated Deprecated in favor of the {@link #requestNetwork},
- * {@link Network#bindProcess} and {@link Network#socketFactory} api.
+ * {@link #setProcessDefaultNetwork} and {@link Network#getSocketFactory} api.
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
InetAddress inetAddress = NetworkUtils.intToInetAddress(hostAddress);
@@ -1059,7 +1101,7 @@
* @return {@code true} on success, {@code false} on failure
* @hide
* @deprecated Deprecated in favor of the {@link #requestNetwork} and
- * {@link Network#bindProcess} api.
+ * {@link #setProcessDefaultNetwork} api.
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
byte[] address = hostAddress.getAddress();
@@ -1142,8 +1184,8 @@
}
/**
- * Callback for use with {@link ConnectivityManager#registerNetworkActiveListener} to
- * find out when the current network has gone in to a high power state.
+ * Callback for use with {@link ConnectivityManager#registerDefaultNetworkActiveListener}
+ * to find out when the system default network has gone in to a high power state.
*/
public interface OnNetworkActiveListener {
/**
@@ -1152,7 +1194,7 @@
* operations. Note that this listener only tells you when the network becomes
* active; if at any other time you want to know whether it is active (and thus okay
* to initiate network traffic), you can retrieve its instantaneous state with
- * {@link ConnectivityManager#isNetworkActive}.
+ * {@link ConnectivityManager#isDefaultNetworkActive}.
*/
public void onNetworkActive();
}
@@ -1173,13 +1215,18 @@
= new ArrayMap<OnNetworkActiveListener, INetworkActivityListener>();
/**
- * Start listening to reports when the data network is active, meaning it is
- * a good time to perform network traffic. Use {@link #isNetworkActive()}
- * to determine the current state of the network after registering the listener.
+ * Start listening to reports when the system's default data network is active, meaning it is
+ * a good time to perform network traffic. Use {@link #isDefaultNetworkActive()}
+ * to determine the current state of the system's default network after registering the
+ * listener.
+ * <p>
+ * If the process default network has been set with
+ * {@link ConnectivityManager#setProcessDefaultNetwork} this function will not
+ * reflect the process's default, but the system default.
*
* @param l The listener to be told when the network is active.
*/
- public void registerNetworkActiveListener(final OnNetworkActiveListener l) {
+ public void registerDefaultNetworkActiveListener(final OnNetworkActiveListener l) {
INetworkActivityListener rl = new INetworkActivityListener.Stub() {
@Override
public void onNetworkActive() throws RemoteException {
@@ -1196,11 +1243,11 @@
/**
* Remove network active listener previously registered with
- * {@link #registerNetworkActiveListener}.
+ * {@link #registerDefaultNetworkActiveListener}.
*
* @param l Previously registered listener.
*/
- public void unregisterNetworkActiveListener(OnNetworkActiveListener l) {
+ public void unregisterDefaultNetworkActiveListener(OnNetworkActiveListener l) {
INetworkActivityListener rl = mNetworkActivityListeners.get(l);
if (rl == null) {
throw new IllegalArgumentException("Listener not registered: " + l);
@@ -1219,7 +1266,7 @@
* this state. This method tells you whether right now is currently a good time to
* initiate network traffic, as the network is already active.
*/
- public boolean isNetworkActive() {
+ public boolean isDefaultNetworkActive() {
try {
return getNetworkManagementService().isNetworkActive();
} catch (RemoteException e) {
@@ -1851,7 +1898,7 @@
* Base class for NetworkRequest callbacks. Used for notifications about network
* changes. Should be extended by applications wanting notifications.
*/
- public static class NetworkCallbackListener {
+ public static class NetworkCallback {
/** @hide */
public static final int PRECHECK = 1;
/** @hide */
@@ -1874,78 +1921,68 @@
* Called whenever the framework connects to a network that it may use to
* satisfy this request
*/
- public void onPreCheck(NetworkRequest networkRequest, Network network) {}
+ public void onPreCheck(Network network) {}
/**
* Called when the framework connects and has declared new network ready for use.
+ * This callback may be called more than once if the {@link Network} that is
+ * satisfying the request changes.
*
- * @param networkRequest The {@link NetworkRequest} used to initiate the request.
* @param network The {@link Network} of the satisfying network.
*/
- public void onAvailable(NetworkRequest networkRequest, Network network) {}
+ public void onAvailable(Network network) {}
/**
* Called when the network is about to be disconnected. Often paired with an
- * {@link NetworkCallbackListener#onAvailable} call with the new replacement network
+ * {@link NetworkCallback#onAvailable} call with the new replacement network
* for graceful handover. This may not be called if we have a hard loss
* (loss without warning). This may be followed by either a
- * {@link NetworkCallbackListener#onLost} call or a
- * {@link NetworkCallbackListener#onAvailable} call for this network depending
+ * {@link NetworkCallback#onLost} call or a
+ * {@link NetworkCallback#onAvailable} call for this network depending
* on whether we lose or regain it.
*
- * @param networkRequest The {@link NetworkRequest} used to initiate the request.
- * @param network The {@link Network} of the failing network.
- * @param maxSecToLive The time in seconds the framework will attempt to keep the
- * network connected. Note that the network may suffers a
+ * @param network The {@link Network} that is about to be disconnected.
+ * @param maxMsToLive The time in ms the framework will attempt to keep the
+ * network connected. Note that the network may suffer a
* hard loss at any time.
*/
- public void onLosing(NetworkRequest networkRequest, Network network, int maxSecToLive) {}
+ public void onLosing(Network network, int maxMsToLive) {}
/**
* Called when the framework has a hard loss of the network or when the
* graceful failure ends.
*
- * @param networkRequest The {@link NetworkRequest} used to initiate the request.
* @param network The {@link Network} lost.
*/
- public void onLost(NetworkRequest networkRequest, Network network) {}
+ public void onLost(Network network) {}
/**
* Called if no network is found in the given timeout time. If no timeout is given,
* this will not be called.
* @hide
*/
- public void onUnavailable(NetworkRequest networkRequest) {}
+ public void onUnavailable() {}
/**
* Called when the network the framework connected to for this request
* changes capabilities but still satisfies the stated need.
*
- * @param networkRequest The {@link NetworkRequest} used to initiate the request.
* @param network The {@link Network} whose capabilities have changed.
* @param networkCapabilities The new {@link NetworkCapabilities} for this network.
*/
- public void onNetworkCapabilitiesChanged(NetworkRequest networkRequest, Network network,
+ public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {}
/**
* Called when the network the framework connected to for this request
* changes {@link LinkProperties}.
*
- * @param networkRequest The {@link NetworkRequest} used to initiate the request.
* @param network The {@link Network} whose link properties have changed.
* @param linkProperties The new {@link LinkProperties} for this network.
*/
- public void onLinkPropertiesChanged(NetworkRequest networkRequest, Network network,
- LinkProperties linkProperties) {}
+ public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {}
- /**
- * Called when a {@link #releaseNetworkRequest} call concludes and the registered
- * callbacks will no longer be used.
- *
- * @param networkRequest The {@link NetworkRequest} used to initiate the request.
- */
- public void onReleased(NetworkRequest networkRequest) {}
+ private NetworkRequest networkRequest;
}
private static final int BASE = Protocol.BASE_CONNECTIVITY_MANAGER;
@@ -1971,12 +2008,12 @@
private static final int EXPIRE_LEGACY_REQUEST = BASE + 10;
private class CallbackHandler extends Handler {
- private final HashMap<NetworkRequest, NetworkCallbackListener>mCallbackMap;
+ private final HashMap<NetworkRequest, NetworkCallback>mCallbackMap;
private final AtomicInteger mRefCount;
private static final String TAG = "ConnectivityManager.CallbackHandler";
private final ConnectivityManager mCm;
- CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallbackListener>callbackMap,
+ CallbackHandler(Looper looper, HashMap<NetworkRequest, NetworkCallback>callbackMap,
AtomicInteger refCount, ConnectivityManager cm) {
super(looper);
mCallbackMap = callbackMap;
@@ -1990,9 +2027,9 @@
switch (message.what) {
case CALLBACK_PRECHECK: {
NetworkRequest request = getNetworkRequest(message);
- NetworkCallbackListener callbacks = getCallbacks(request);
+ NetworkCallback callbacks = getCallbacks(request);
if (callbacks != null) {
- callbacks.onPreCheck(request, getNetwork(message));
+ callbacks.onPreCheck(getNetwork(message));
} else {
Log.e(TAG, "callback not found for PRECHECK message");
}
@@ -2000,9 +2037,9 @@
}
case CALLBACK_AVAILABLE: {
NetworkRequest request = getNetworkRequest(message);
- NetworkCallbackListener callbacks = getCallbacks(request);
+ NetworkCallback callbacks = getCallbacks(request);
if (callbacks != null) {
- callbacks.onAvailable(request, getNetwork(message));
+ callbacks.onAvailable(getNetwork(message));
} else {
Log.e(TAG, "callback not found for AVAILABLE message");
}
@@ -2010,9 +2047,9 @@
}
case CALLBACK_LOSING: {
NetworkRequest request = getNetworkRequest(message);
- NetworkCallbackListener callbacks = getCallbacks(request);
+ NetworkCallback callbacks = getCallbacks(request);
if (callbacks != null) {
- callbacks.onLosing(request, getNetwork(message), message.arg1);
+ callbacks.onLosing(getNetwork(message), message.arg1);
} else {
Log.e(TAG, "callback not found for LOSING message");
}
@@ -2020,9 +2057,9 @@
}
case CALLBACK_LOST: {
NetworkRequest request = getNetworkRequest(message);
- NetworkCallbackListener callbacks = getCallbacks(request);
+ NetworkCallback callbacks = getCallbacks(request);
if (callbacks != null) {
- callbacks.onLost(request, getNetwork(message));
+ callbacks.onLost(getNetwork(message));
} else {
Log.e(TAG, "callback not found for LOST message");
}
@@ -2030,12 +2067,12 @@
}
case CALLBACK_UNAVAIL: {
NetworkRequest req = (NetworkRequest)message.obj;
- NetworkCallbackListener callbacks = null;
+ NetworkCallback callbacks = null;
synchronized(mCallbackMap) {
callbacks = mCallbackMap.get(req);
}
if (callbacks != null) {
- callbacks.onUnavailable(req);
+ callbacks.onUnavailable();
} else {
Log.e(TAG, "callback not found for UNAVAIL message");
}
@@ -2043,12 +2080,12 @@
}
case CALLBACK_CAP_CHANGED: {
NetworkRequest request = getNetworkRequest(message);
- NetworkCallbackListener callbacks = getCallbacks(request);
+ NetworkCallback callbacks = getCallbacks(request);
if (callbacks != null) {
Network network = getNetwork(message);
NetworkCapabilities cap = mCm.getNetworkCapabilities(network);
- callbacks.onNetworkCapabilitiesChanged(request, network, cap);
+ callbacks.onCapabilitiesChanged(network, cap);
} else {
Log.e(TAG, "callback not found for CHANGED message");
}
@@ -2056,12 +2093,12 @@
}
case CALLBACK_IP_CHANGED: {
NetworkRequest request = getNetworkRequest(message);
- NetworkCallbackListener callbacks = getCallbacks(request);
+ NetworkCallback callbacks = getCallbacks(request);
if (callbacks != null) {
Network network = getNetwork(message);
LinkProperties lp = mCm.getLinkProperties(network);
- callbacks.onLinkPropertiesChanged(request, network, lp);
+ callbacks.onLinkPropertiesChanged(network, lp);
} else {
Log.e(TAG, "callback not found for CHANGED message");
}
@@ -2069,20 +2106,19 @@
}
case CALLBACK_RELEASED: {
NetworkRequest req = (NetworkRequest)message.obj;
- NetworkCallbackListener callbacks = null;
+ NetworkCallback callbacks = null;
synchronized(mCallbackMap) {
callbacks = mCallbackMap.remove(req);
}
if (callbacks != null) {
- callbacks.onReleased(req);
+ synchronized(mRefCount) {
+ if (mRefCount.decrementAndGet() == 0) {
+ getLooper().quit();
+ }
+ }
} else {
Log.e(TAG, "callback not found for CANCELED message");
}
- synchronized(mRefCount) {
- if (mRefCount.decrementAndGet() == 0) {
- getLooper().quit();
- }
- }
break;
}
case CALLBACK_EXIT: {
@@ -2100,7 +2136,7 @@
private NetworkRequest getNetworkRequest(Message msg) {
return (NetworkRequest)(msg.obj);
}
- private NetworkCallbackListener getCallbacks(NetworkRequest req) {
+ private NetworkCallback getCallbacks(NetworkRequest req) {
synchronized(mCallbackMap) {
return mCallbackMap.get(req);
}
@@ -2108,7 +2144,7 @@
private Network getNetwork(Message msg) {
return new Network(msg.arg2);
}
- private NetworkCallbackListener removeCallbacks(Message msg) {
+ private NetworkCallback removeCallbacks(Message msg) {
NetworkRequest req = (NetworkRequest)msg.obj;
synchronized(mCallbackMap) {
return mCallbackMap.remove(req);
@@ -2116,19 +2152,19 @@
}
}
- private void addCallbackListener() {
+ private void incCallbackHandlerRefCount() {
synchronized(sCallbackRefCount) {
if (sCallbackRefCount.incrementAndGet() == 1) {
// TODO - switch this over to a ManagerThread or expire it when done
HandlerThread callbackThread = new HandlerThread("ConnectivityManager");
callbackThread.start();
sCallbackHandler = new CallbackHandler(callbackThread.getLooper(),
- sNetworkCallbackListener, sCallbackRefCount, this);
+ sNetworkCallback, sCallbackRefCount, this);
}
}
}
- private void removeCallbackListener() {
+ private void decCallbackHandlerRefCount() {
synchronized(sCallbackRefCount) {
if (sCallbackRefCount.decrementAndGet() == 0) {
sCallbackHandler.obtainMessage(CALLBACK_EXIT).sendToTarget();
@@ -2137,8 +2173,8 @@
}
}
- static final HashMap<NetworkRequest, NetworkCallbackListener> sNetworkCallbackListener =
- new HashMap<NetworkRequest, NetworkCallbackListener>();
+ static final HashMap<NetworkRequest, NetworkCallback> sNetworkCallback =
+ new HashMap<NetworkRequest, NetworkCallback>();
static final AtomicInteger sCallbackRefCount = new AtomicInteger(0);
static CallbackHandler sCallbackHandler = null;
@@ -2146,51 +2182,48 @@
private final static int REQUEST = 2;
private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
- NetworkCallbackListener networkCallbackListener, int timeoutSec, int action,
+ NetworkCallback networkCallback, int timeoutSec, int action,
int legacyType) {
- NetworkRequest networkRequest = null;
- if (networkCallbackListener == null) {
- throw new IllegalArgumentException("null NetworkCallbackListener");
+ if (networkCallback == null) {
+ throw new IllegalArgumentException("null NetworkCallback");
}
if (need == null) throw new IllegalArgumentException("null NetworkCapabilities");
try {
- addCallbackListener();
+ incCallbackHandlerRefCount();
if (action == LISTEN) {
- networkRequest = mService.listenForNetwork(need, new Messenger(sCallbackHandler),
- new Binder());
+ networkCallback.networkRequest = mService.listenForNetwork(need,
+ new Messenger(sCallbackHandler), new Binder());
} else {
- networkRequest = mService.requestNetwork(need, new Messenger(sCallbackHandler),
- timeoutSec, new Binder(), legacyType);
+ networkCallback.networkRequest = mService.requestNetwork(need,
+ new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType);
}
- if (networkRequest != null) {
- synchronized(sNetworkCallbackListener) {
- sNetworkCallbackListener.put(networkRequest, networkCallbackListener);
+ if (networkCallback.networkRequest != null) {
+ synchronized(sNetworkCallback) {
+ sNetworkCallback.put(networkCallback.networkRequest, networkCallback);
}
}
} catch (RemoteException e) {}
- if (networkRequest == null) removeCallbackListener();
- return networkRequest;
+ if (networkCallback.networkRequest == null) decCallbackHandlerRefCount();
+ return networkCallback.networkRequest;
}
/**
* Request a network to satisfy a set of {@link NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
- * {@link #releaseNetworkRequest} or the calling application exits.
+ * {@link #unregisterNetworkCallback} or the calling application exits.
* Status of the request can be followed by listening to the various
- * callbacks described in {@link NetworkCallbackListener}. The {@link Network}
+ * callbacks described in {@link NetworkCallback}. The {@link Network}
* can be used to direct traffic to the network.
*
- * @param need {@link NetworkCapabilities} required by this request.
- * @param networkCallbackListener The {@link NetworkCallbackListener} to be utilized for this
- * request. Note the callbacks can be shared by multiple
- * requests and the NetworkRequest token utilized to
- * determine to which request the callback relates.
- * @return A {@link NetworkRequest} object identifying the request.
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this
+ * request. Note the callback must not be shared - they
+ * uniquely specify this request.
*/
- public NetworkRequest requestNetwork(NetworkCapabilities need,
- NetworkCallbackListener networkCallbackListener) {
- return sendRequestForNetwork(need, networkCallbackListener, 0, REQUEST, TYPE_NONE);
+ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
+ sendRequestForNetwork(request.networkCapabilities, networkCallback, 0,
+ REQUEST, TYPE_NONE);
}
/**
@@ -2198,53 +2231,53 @@
* by a timeout.
*
* This function behaves identically to the non-timedout version, but if a suitable
- * network is not found within the given time (in Seconds) the
- * {@link NetworkCallbackListener#unavailable} callback is called. The request must
+ * network is not found within the given time (in milliseconds) the
+ * {@link NetworkCallback#unavailable} callback is called. The request must
* still be released normally by calling {@link releaseNetworkRequest}.
- * @param need {@link NetworkCapabilities} required by this request.
- * @param networkCallbackListener The callbacks to be utilized for this request. Note
- * the callbacks can be shared by multiple requests and
- * the NetworkRequest token utilized to determine to which
- * request the callback relates.
- * @param timeoutSec The time in seconds to attempt looking for a suitable network
- * before {@link NetworkCallbackListener#unavailable} is called.
- * @return A {@link NetworkRequest} object identifying the request.
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The callbacks to be utilized for this request. Note
+ * the callbacks must not be shared - they uniquely specify
+ * this request.
+ * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+ * before {@link NetworkCallback#unavailable} is called.
* @hide
*/
- public NetworkRequest requestNetwork(NetworkCapabilities need,
- NetworkCallbackListener networkCallbackListener, int timeoutSec) {
- return sendRequestForNetwork(need, networkCallbackListener, timeoutSec, REQUEST,
- TYPE_NONE);
+ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
+ int timeoutMs) {
+ sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs,
+ REQUEST, TYPE_NONE);
}
/**
- * The maximum number of seconds the framework will look for a suitable network
+ * The maximum number of milliseconds the framework will look for a suitable network
* during a timeout-equiped call to {@link requestNetwork}.
* {@hide}
*/
- public final static int MAX_NETWORK_REQUEST_TIMEOUT_SEC = 100 * 60;
+ public final static int MAX_NETWORK_REQUEST_TIMEOUT_MS = 100 * 60 * 1000;
/**
* The lookup key for a {@link Network} object included with the intent after
* succesfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
*/
public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
/**
- * The lookup key for a {@link NetworkCapabilities} object included with the intent after
+ * The lookup key for a {@link NetworkRequest} object included with the intent after
* succesfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
*/
- public static final String EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES =
- "networkRequestNetworkCapabilities";
+ public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST =
+ "networkRequestNetworkRequest";
/**
* Request a network to satisfy a set of {@link NetworkCapabilities}.
*
- * This function behavies identically to the callback-equiped version, but instead
- * of {@link NetworkCallbackListener} a {@link PendingIntent} is used. This means
+ * This function behavies identically to the version that takes a NetworkCallback, but instead
+ * of {@link NetworkCallback} a {@link PendingIntent} is used. This means
* the request may outlive the calling application and get called back when a suitable
* network is found.
* <p>
@@ -2253,10 +2286,10 @@
* <receiver> tag in an AndroidManifest.xml file
* <p>
* The operation Intent is delivered with two extras, a {@link Network} typed
- * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkCapabilities}
- * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_CAPABILITIES} containing
+ * extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK} and a {@link NetworkRequest}
+ * typed extra called {@link #EXTRA_NETWORK_REQUEST_NETWORK_REQUEST} containing
* the original requests parameters. It is important to create a new,
- * {@link NetworkCallbackListener} based request before completing the processing of the
+ * {@link NetworkCallback} based request before completing the processing of the
* Intent to reserve the network or it will be released shortly after the Intent
* is processed.
* <p>
@@ -2264,51 +2297,109 @@
* two Intents defined by {@link Intent#filterEquals}), then it will be removed and
* replaced by this one, effectively releasing the previous {@link NetworkRequest}.
* <p>
- * The request may be released normally by calling {@link #releaseNetworkRequest}.
+ * The request may be released normally by calling {@link #unregisterNetworkCallback}.
*
- * @param need {@link NetworkCapabilities} required by this request.
+ * @param request {@link NetworkRequest} describing this request.
* @param operation Action to perform when the network is available (corresponds
- * to the {@link NetworkCallbackListener#onAvailable} call. Typically
+ * to the {@link NetworkCallback#onAvailable} call. Typically
* comes from {@link PendingIntent#getBroadcast}.
- * @return A {@link NetworkRequest} object identifying the request.
+ * @hide
*/
- public NetworkRequest requestNetwork(NetworkCapabilities need, PendingIntent operation) {
+ public void requestNetwork(NetworkRequest request, PendingIntent operation) {
try {
- return mService.pendingRequestForNetwork(need, operation);
+ mService.pendingRequestForNetwork(request.networkCapabilities, operation);
} catch (RemoteException e) {}
- return null;
}
/**
* Registers to receive notifications about all networks which satisfy the given
- * {@link NetworkCapabilities}. The callbacks will continue to be called until
- * either the application exits or the request is released using
- * {@link #releaseNetworkRequest}.
+ * {@link NetworkRequest}. The callbacks will continue to be called until
+ * either the application exits or {@link #unregisterNetworkCallback} is called
*
- * @param need {@link NetworkCapabilities} required by this request.
- * @param networkCallbackListener The {@link NetworkCallbackListener} to be called as suitable
- * networks change state.
- * @return A {@link NetworkRequest} object identifying the request.
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
+ * networks change state.
*/
- public NetworkRequest listenForNetwork(NetworkCapabilities need,
- NetworkCallbackListener networkCallbackListener) {
- return sendRequestForNetwork(need, networkCallbackListener, 0, LISTEN, TYPE_NONE);
+ public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) {
+ sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, LISTEN, TYPE_NONE);
}
/**
- * Releases a {@link NetworkRequest} generated either through a {@link #requestNetwork}
- * or a {@link #listenForNetwork} call. The {@link NetworkCallbackListener} given in the
- * earlier call may continue receiving calls until the
- * {@link NetworkCallbackListener#onReleased} function is called, signifying the end
- * of the request.
+ * Unregisters callbacks about and possibly releases networks originating from
+ * {@link #requestNetwork} and {@link #registerNetworkCallback} calls. If the
+ * given {@code NetworkCallback} had previosuly been used with {@code #requestNetwork},
+ * any networks that had been connected to only to satisfy that request will be
+ * disconnected.
*
- * @param networkRequest The {@link NetworkRequest} generated by an earlier call to
- * {@link #requestNetwork} or {@link #listenForNetwork}.
+ * @param networkCallback The {@link NetworkCallback} used when making the request.
*/
- public void releaseNetworkRequest(NetworkRequest networkRequest) {
- if (networkRequest == null) throw new IllegalArgumentException("null NetworkRequest");
+ public void unregisterNetworkCallback(NetworkCallback networkCallback) {
+ if (networkCallback == null || networkCallback.networkRequest == null ||
+ networkCallback.networkRequest.requestId == REQUEST_ID_UNSET) {
+ throw new IllegalArgumentException("Invalid NetworkCallback");
+ }
try {
- mService.releaseNetworkRequest(networkRequest);
+ mService.releaseNetworkRequest(networkCallback.networkRequest);
} catch (RemoteException e) {}
}
+
+ /**
+ * Binds the current process to {@code network}. All Sockets created in the future
+ * (and not explicitly bound via a bound SocketFactory from
+ * {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
+ * {@code network}. All host name resolutions will be limited to {@code network} as well.
+ * Note that if {@code network} ever disconnects, all Sockets created in this way will cease to
+ * work and all host name resolutions will fail. This is by design so an application doesn't
+ * accidentally use Sockets it thinks are still bound to a particular {@link Network}.
+ * To clear binding pass {@code null} for {@code network}. Using individually bound
+ * Sockets created by Network.getSocketFactory().createSocket() and
+ * performing network-specific host name resolutions via
+ * {@link Network#getAllByName Network.getAllByName} is preferred to calling
+ * {@code setProcessDefaultNetwork}.
+ *
+ * @param network The {@link Network} to bind the current process to, or {@code null} to clear
+ * the current binding.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ */
+ public static boolean setProcessDefaultNetwork(Network network) {
+ if (network == null) {
+ NetworkUtils.unbindProcessToNetwork();
+ } else {
+ NetworkUtils.bindProcessToNetwork(network.netId);
+ }
+ // TODO fix return value
+ return true;
+ }
+
+ /**
+ * Returns the {@link Network} currently bound to this process via
+ * {@link #setProcessDefaultNetwork}, or {@code null} if no {@link Network} is explicitly bound.
+ *
+ * @return {@code Network} to which this process is bound, or {@code null}.
+ */
+ public static Network getProcessDefaultNetwork() {
+ int netId = NetworkUtils.getNetworkBoundToProcess();
+ if (netId == 0) return null;
+ return new Network(netId);
+ }
+
+ /**
+ * Binds host resolutions performed by this process to {@code network}.
+ * {@link #setProcessDefaultNetwork} takes precedence over this setting.
+ *
+ * @param network The {@link Network} to bind host resolutions from the current process to, or
+ * {@code null} to clear the current binding.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ * @hide
+ * @deprecated This is strictly for legacy usage to support {@link #startUsingNetworkFeature}.
+ */
+ public static boolean setProcessDefaultNetworkForHostResolution(Network network) {
+ if (network == null) {
+ NetworkUtils.unbindProcessToNetworkForHostResolution();
+ } else {
+ NetworkUtils.bindProcessToNetworkForHostResolution(network.netId);
+ }
+ // TODO hook up the return value.
+ return true;
+ }
}
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 22b26b1..49a307e 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -74,8 +74,10 @@
if (linkProperties.getRoutes().size() == 0) {
for (RouteInfo r : orig.linkProperties.getRoutes()) linkProperties.addRoute(r);
}
- if (linkProperties.getDnses().size() == 0) {
- for (InetAddress d : orig.linkProperties.getDnses()) linkProperties.addDns(d);
+ if (linkProperties.getDnsServers().size() == 0) {
+ for (InetAddress d : orig.linkProperties.getDnsServers()) {
+ linkProperties.addDnsServer(d);
+ }
}
}
@@ -211,7 +213,7 @@
public boolean addDns(String addrString) {
if (TextUtils.isEmpty(addrString) == false) {
try {
- linkProperties.addDns(NetworkUtils.numericToInetAddress(addrString));
+ linkProperties.addDnsServer(NetworkUtils.numericToInetAddress(addrString));
} catch (IllegalArgumentException e) {
Log.e(TAG, "addDns failed with addrString " + addrString);
return true;
diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java
index 66f0fd0..7acf3f5 100644
--- a/core/java/android/net/DnsPinger.java
+++ b/core/java/android/net/DnsPinger.java
@@ -248,7 +248,7 @@
return mDefaultDns;
}
- Collection<InetAddress> dnses = curLinkProps.getDnses();
+ Collection<InetAddress> dnses = curLinkProps.getDnsServers();
if (dnses == null || dnses.size() == 0) {
loge("getDns::LinkProps has null dns - returning default");
return mDefaultDns;
diff --git a/core/java/android/net/IpPrefix.aidl b/core/java/android/net/IpPrefix.aidl
new file mode 100644
index 0000000..9e552c7
--- /dev/null
+++ b/core/java/android/net/IpPrefix.aidl
@@ -0,0 +1,20 @@
+/**
+ *
+ * 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.net;
+
+parcelable IpPrefix;
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
new file mode 100644
index 0000000..a14d13f
--- /dev/null
+++ b/core/java/android/net/IpPrefix.java
@@ -0,0 +1,170 @@
+/*
+ * 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+/**
+ * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a
+ * power of two boundary (also known as an "IP subnet"). A prefix is specified by two pieces of
+ * information:
+ *
+ * <ul>
+ * <li>A starting IP address (IPv4 or IPv6). This is the first IP address of the prefix.
+ * <li>A prefix length. This specifies the length of the prefix by specifing the number of bits
+ * in the IP address, starting from the most significant bit in network byte order, that
+ * are constant for all addresses in the prefix.
+ * </ul>
+ *
+ * For example, the prefix <code>192.0.2.0/24</code> covers the 256 IPv4 addresses from
+ * <code>192.0.2.0</code> to <code>192.0.2.255</code>, inclusive, and the prefix
+ * <code>2001:db8:1:2</code> covers the 2^64 IPv6 addresses from <code>2001:db8:1:2::</code> to
+ * <code>2001:db8:1:2:ffff:ffff:ffff:ffff</code>, inclusive.
+ *
+ * Objects of this class are immutable.
+ */
+public final class IpPrefix implements Parcelable {
+ private final byte[] address; // network byte order
+ private final int prefixLength;
+
+ /**
+ * Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in
+ * network byte order and a prefix length.
+ *
+ * @param address the IP address. Must be non-null and exactly 4 or 16 bytes long.
+ * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
+ *
+ * @hide
+ */
+ public IpPrefix(byte[] address, int prefixLength) {
+ if (address.length != 4 && address.length != 16) {
+ throw new IllegalArgumentException(
+ "IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
+ }
+ if (prefixLength < 0 || prefixLength > (address.length * 8)) {
+ throw new IllegalArgumentException("IpPrefix with " + address.length +
+ " bytes has invalid prefix length " + prefixLength);
+ }
+ this.address = address.clone();
+ this.prefixLength = prefixLength;
+ // TODO: Validate that the non-prefix bits are zero
+ }
+
+ /**
+ * @hide
+ */
+ public IpPrefix(InetAddress address, int prefixLength) {
+ this(address.getAddress(), prefixLength);
+ }
+
+ /**
+ * Compares this {@code IpPrefix} object against the specified object in {@code obj}. Two
+ * objects are equal if they have the same startAddress and prefixLength.
+ *
+ * @param obj the object to be tested for equality.
+ * @return {@code true} if both objects are equal, {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof IpPrefix)) {
+ return false;
+ }
+ IpPrefix that = (IpPrefix) obj;
+ return Arrays.equals(this.address, that.address) && this.prefixLength == that.prefixLength;
+ }
+
+ /**
+ * Gets the hashcode of the represented IP prefix.
+ *
+ * @return the appropriate hashcode value.
+ */
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(address) + 11 * prefixLength;
+ }
+
+ /**
+ * Returns a copy of the first IP address in the prefix. Modifying the returned object does not
+ * change this object's contents.
+ *
+ * @return the address in the form of a byte array.
+ */
+ public InetAddress getAddress() {
+ try {
+ return InetAddress.getByAddress(address);
+ } catch (UnknownHostException e) {
+ // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte
+ // array is the wrong length, but we check that in the constructor.
+ return null;
+ }
+ }
+
+ /**
+ * Returns a copy of the IP address bytes in network order (the highest order byte is the zeroth
+ * element). Modifying the returned array does not change this object's contents.
+ *
+ * @return the address in the form of a byte array.
+ */
+ public byte[] getRawAddress() {
+ return address.clone();
+ }
+
+ /**
+ * Returns the prefix length of this {@code IpAddress}.
+ *
+ * @return the prefix length.
+ */
+ public int getPrefixLength() {
+ return prefixLength;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByteArray(address);
+ dest.writeInt(prefixLength);
+ }
+
+ /**
+ * Implement the Parcelable interface.
+ */
+ public static final Creator<IpPrefix> CREATOR =
+ new Creator<IpPrefix>() {
+ public IpPrefix createFromParcel(Parcel in) {
+ byte[] address = in.createByteArray();
+ int prefixLength = in.readInt();
+ return new IpPrefix(address, prefixLength);
+ }
+
+ public IpPrefix[] newArray(int size) {
+ return new IpPrefix[size];
+ }
+ };
+}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index d07c0b61..5246078 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -39,18 +39,13 @@
* <ul>
* <li>An IP address and prefix length (e.g., {@code 2001:db8::1/64} or {@code 192.0.2.1/24}).
* The address must be unicast, as multicast addresses cannot be assigned to interfaces.
- * <li>Address flags: A bitmask of {@code IFA_F_*} values representing properties
- * of the address.
- * <li>Address scope: An integer defining the scope in which the address is unique (e.g.,
- * {@code RT_SCOPE_LINK} or {@code RT_SCOPE_SITE}).
- * <ul>
- *<p>
- * When constructing a {@code LinkAddress}, the IP address and prefix are required. The flags and
- * scope are optional. If they are not specified, the flags are set to zero, and the scope will be
- * determined based on the IP address (e.g., link-local addresses will be created with a scope of
- * {@code RT_SCOPE_LINK}, global addresses with {@code RT_SCOPE_UNIVERSE},
- * etc.) If they are specified, they are not checked for validity.
- *
+ * <li>Address flags: A bitmask of {@code OsConstants.IFA_F_*} values representing properties
+ * of the address (e.g., {@code android.system.OsConstants.IFA_F_OPTIMISTIC}).
+ * <li>Address scope: One of the {@code OsConstants.IFA_F_*} values; defines the scope in which
+ * the address is unique (e.g.,
+ * {@code android.system.OsConstants.RT_SCOPE_LINK} or
+ * {@code android.system.OsConstants.RT_SCOPE_UNIVERSE}).
+ * </ul>
*/
public class LinkAddress implements Parcelable {
/**
@@ -202,7 +197,9 @@
/**
* Compares this {@code LinkAddress} instance against {@code obj}. Two addresses are equal if
- * their address, prefix length, flags and scope are equal.
+ * their address, prefix length, flags and scope are equal. Thus, for example, two addresses
+ * that have the same address and prefix length are not equal if one of them is deprecated and
+ * the other is not.
*
* @param obj the object to be tested for equality.
* @return {@code true} if both objects are equal, {@code false} otherwise.
@@ -236,6 +233,7 @@
* @param other the {@code LinkAddress} to compare to.
* @return {@code true} if both objects have the same address and prefix length, {@code false}
* otherwise.
+ * @hide
*/
public boolean isSameAddressAs(LinkAddress other) {
return address.equals(other.address) && prefixLength == other.prefixLength;
@@ -251,11 +249,20 @@
/**
* Returns the prefix length of this {@code LinkAddress}.
*/
- public int getNetworkPrefixLength() {
+ public int getPrefixLength() {
return prefixLength;
}
/**
+ * Returns the prefix length of this {@code LinkAddress}.
+ * TODO: Delete all callers and remove in favour of getPrefixLength().
+ * @hide
+ */
+ public int getNetworkPrefixLength() {
+ return getPrefixLength();
+ }
+
+ /**
* Returns the flags of this {@code LinkAddress}.
*/
public int getFlags() {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 3c36679..8eefa0f 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -30,6 +30,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Hashtable;
+import java.util.List;
/**
* Describes the properties of a network link.
@@ -43,7 +44,7 @@
* does not affect live networks.
*
*/
-public class LinkProperties implements Parcelable {
+public final class LinkProperties implements Parcelable {
// The interface described by the network link.
private String mIfaceName;
private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>();
@@ -58,10 +59,12 @@
private Hashtable<String, LinkProperties> mStackedLinks =
new Hashtable<String, LinkProperties>();
- // @hide
+ /**
+ * @hide
+ */
public static class CompareResult<T> {
- public Collection<T> removed = new ArrayList<T>();
- public Collection<T> added = new ArrayList<T>();
+ public List<T> removed = new ArrayList<T>();
+ public List<T> added = new ArrayList<T>();
@Override
public String toString() {
@@ -74,14 +77,20 @@
}
}
+ /**
+ * @hide
+ */
public LinkProperties() {
}
+ /**
+ * @hide
+ */
public LinkProperties(LinkProperties source) {
if (source != null) {
mIfaceName = source.getInterfaceName();
for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l);
- for (InetAddress i : source.getDnses()) mDnses.add(i);
+ for (InetAddress i : source.getDnsServers()) mDnses.add(i);
mDomains = source.getDomains();
for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
mHttpProxy = (source.getHttpProxy() == null) ?
@@ -98,6 +107,7 @@
* will have their interface changed to match this new value.
*
* @param iface The name of the network interface used for this link.
+ * @hide
*/
public void setInterfaceName(String iface) {
mIfaceName = iface;
@@ -117,9 +127,11 @@
return mIfaceName;
}
- // @hide
- public Collection<String> getAllInterfaceNames() {
- Collection interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
+ /**
+ * @hide
+ */
+ public List<String> getAllInterfaceNames() {
+ List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
for (LinkProperties stacked: mStackedLinks.values()) {
interfaceNames.addAll(stacked.getAllInterfaceNames());
@@ -134,23 +146,23 @@
* prefix lengths for each address. This is a simplified utility alternative to
* {@link LinkProperties#getLinkAddresses}.
*
- * @return An umodifiable {@link Collection} of {@link InetAddress} for this link.
+ * @return An umodifiable {@link List} of {@link InetAddress} for this link.
* @hide
*/
- public Collection<InetAddress> getAddresses() {
- Collection<InetAddress> addresses = new ArrayList<InetAddress>();
+ public List<InetAddress> getAddresses() {
+ List<InetAddress> addresses = new ArrayList<InetAddress>();
for (LinkAddress linkAddress : mLinkAddresses) {
addresses.add(linkAddress.getAddress());
}
- return Collections.unmodifiableCollection(addresses);
+ return Collections.unmodifiableList(addresses);
}
/**
* Returns all the addresses on this link and all the links stacked above it.
* @hide
*/
- public Collection<InetAddress> getAllAddresses() {
- Collection<InetAddress> addresses = new ArrayList<InetAddress>();
+ public List<InetAddress> getAllAddresses() {
+ List<InetAddress> addresses = new ArrayList<InetAddress>();
for (LinkAddress linkAddress : mLinkAddresses) {
addresses.add(linkAddress.getAddress());
}
@@ -174,6 +186,7 @@
* same address/prefix does not already exist. If it does exist it is replaced.
* @param address The {@code LinkAddress} to add.
* @return true if {@code address} was added or updated, false otherwise.
+ * @hide
*/
public boolean addLinkAddress(LinkAddress address) {
if (address == null) {
@@ -200,6 +213,7 @@
*
* @param toRemove A {@link LinkAddress} specifying the address to remove.
* @return true if the address was removed, false if it did not exist.
+ * @hide
*/
public boolean removeLinkAddress(LinkAddress toRemove) {
int i = findLinkAddressIndex(toRemove);
@@ -214,18 +228,18 @@
* Returns all the {@link LinkAddress} on this link. Typically a link will have
* one IPv4 address and one or more IPv6 addresses.
*
- * @return An unmodifiable {@link Collection} of {@link LinkAddress} for this link.
+ * @return An unmodifiable {@link List} of {@link LinkAddress} for this link.
*/
- public Collection<LinkAddress> getLinkAddresses() {
- return Collections.unmodifiableCollection(mLinkAddresses);
+ public List<LinkAddress> getLinkAddresses() {
+ return Collections.unmodifiableList(mLinkAddresses);
}
/**
* Returns all the addresses on this link and all the links stacked above it.
* @hide
*/
- public Collection<LinkAddress> getAllLinkAddresses() {
- Collection<LinkAddress> addresses = new ArrayList<LinkAddress>();
+ public List<LinkAddress> getAllLinkAddresses() {
+ List<LinkAddress> addresses = new ArrayList<LinkAddress>();
addresses.addAll(mLinkAddresses);
for (LinkProperties stacked: mStackedLinks.values()) {
addresses.addAll(stacked.getAllLinkAddresses());
@@ -239,6 +253,7 @@
*
* @param addresses The {@link Collection} of {@link LinkAddress} to set in this
* object.
+ * @hide
*/
public void setLinkAddresses(Collection<LinkAddress> addresses) {
mLinkAddresses.clear();
@@ -250,20 +265,21 @@
/**
* Adds the given {@link InetAddress} to the list of DNS servers.
*
- * @param dns The {@link InetAddress} to add to the list of DNS servers.
+ * @param dnsServer The {@link InetAddress} to add to the list of DNS servers.
+ * @hide
*/
- public void addDns(InetAddress dns) {
- if (dns != null) mDnses.add(dns);
+ public void addDnsServer(InetAddress dnsServer) {
+ if (dnsServer != null) mDnses.add(dnsServer);
}
/**
- * Returns all the {@link LinkAddress} for DNS servers on this link.
+ * Returns all the {@link InetAddress} for DNS servers on this link.
*
- * @return An umodifiable {@link Collection} of {@link InetAddress} for DNS servers on
+ * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on
* this link.
*/
- public Collection<InetAddress> getDnses() {
- return Collections.unmodifiableCollection(mDnses);
+ public List<InetAddress> getDnsServers() {
+ return Collections.unmodifiableList(mDnses);
}
/**
@@ -271,6 +287,7 @@
*
* @param domains A {@link String} listing in priority order the comma separated
* domains to search when resolving host names on this link.
+ * @hide
*/
public void setDomains(String domains) {
mDomains = domains;
@@ -323,6 +340,7 @@
* proper course is to add either un-named or properly named {@link RouteInfo}.
*
* @param route A {@link RouteInfo} to add to this object.
+ * @hide
*/
public void addRoute(RouteInfo route) {
if (route != null) {
@@ -339,18 +357,18 @@
/**
* Returns all the {@link RouteInfo} set on this link.
*
- * @return An unmodifiable {@link Collection} of {@link RouteInfo} for this link.
+ * @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
*/
- public Collection<RouteInfo> getRoutes() {
- return Collections.unmodifiableCollection(mRoutes);
+ public List<RouteInfo> getRoutes() {
+ return Collections.unmodifiableList(mRoutes);
}
/**
* Returns all the routes on this link and all the links stacked above it.
* @hide
*/
- public Collection<RouteInfo> getAllRoutes() {
- Collection<RouteInfo> routes = new ArrayList();
+ public List<RouteInfo> getAllRoutes() {
+ List<RouteInfo> routes = new ArrayList();
routes.addAll(mRoutes);
for (LinkProperties stacked: mStackedLinks.values()) {
routes.addAll(stacked.getAllRoutes());
@@ -364,6 +382,7 @@
* not enforce it and applications may ignore them.
*
* @param proxy A {@link ProxyInfo} defining the Http Proxy to use on this link.
+ * @hide
*/
public void setHttpProxy(ProxyInfo proxy) {
mHttpProxy = proxy;
@@ -419,16 +438,17 @@
* Returns all the links stacked on top of this link.
* @hide
*/
- public Collection<LinkProperties> getStackedLinks() {
- Collection<LinkProperties> stacked = new ArrayList<LinkProperties>();
+ public List<LinkProperties> getStackedLinks() {
+ List<LinkProperties> stacked = new ArrayList<LinkProperties>();
for (LinkProperties link : mStackedLinks.values()) {
stacked.add(new LinkProperties(link));
}
- return Collections.unmodifiableCollection(stacked);
+ return Collections.unmodifiableList(stacked);
}
/**
* Clears this object to its initial state.
+ * @hide
*/
public void clear() {
mIfaceName = null;
@@ -443,7 +463,6 @@
/**
* Implement the Parcelable interface
- * @hide
*/
public int describeContents() {
return 0;
@@ -463,12 +482,12 @@
String domainName = "Domains: " + mDomains;
- String mtu = "MTU: " + mMtu;
+ String mtu = " MTU: " + mMtu;
String routes = " Routes: [";
for (RouteInfo route : mRoutes) routes += route.toString() + ",";
routes += "] ";
- String proxy = (mHttpProxy == null ? "" : "HttpProxy: " + mHttpProxy.toString() + " ");
+ String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " ");
String stacked = "";
if (mStackedLinks.values().size() > 0) {
@@ -486,6 +505,7 @@
* Returns true if this link has an IPv4 address.
*
* @return {@code true} if there is an IPv4 address, {@code false} otherwise.
+ * @hide
*/
public boolean hasIPv4Address() {
for (LinkAddress address : mLinkAddresses) {
@@ -500,6 +520,7 @@
* Returns true if this link has an IPv6 address.
*
* @return {@code true} if there is an IPv6 address, {@code false} otherwise.
+ * @hide
*/
public boolean hasIPv6Address() {
for (LinkAddress address : mLinkAddresses) {
@@ -543,7 +564,7 @@
* @hide
*/
public boolean isIdenticalDnses(LinkProperties target) {
- Collection<InetAddress> targetDnses = target.getDnses();
+ Collection<InetAddress> targetDnses = target.getDnsServers();
String targetDomains = target.getDomains();
if (mDomains == null) {
if (targetDomains != null) return false;
@@ -696,7 +717,7 @@
result.removed = new ArrayList<InetAddress>(mDnses);
result.added.clear();
if (target != null) {
- for (InetAddress newAddress : target.getDnses()) {
+ for (InetAddress newAddress : target.getDnsServers()) {
if (! result.removed.remove(newAddress)) {
result.added.add(newAddress);
}
@@ -831,7 +852,7 @@
addressCount = in.readInt();
for (int i=0; i<addressCount; i++) {
try {
- netProp.addDns(InetAddress.getByAddress(in.createByteArray()));
+ netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray()));
} catch (UnknownHostException e) { }
}
netProp.setDomains(in.readString());
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 64516e6..318aabe 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -29,10 +29,12 @@
/**
* Identifies a {@code Network}. This is supplied to applications via
- * {@link ConnectivityManager.NetworkCallbackListener} in response to
- * {@link ConnectivityManager#requestNetwork} or {@link ConnectivityManager#listenForNetwork}.
+ * {@link ConnectivityManager.NetworkCallback} in response to the active
+ * {@link ConnectivityManager#requestNetwork} or passive
+ * {@link ConnectivityManager#registerNetworkCallback} calls.
* It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis
- * through a targeted {@link SocketFactory} or process-wide via {@link #bindProcess}.
+ * through a targeted {@link SocketFactory} or process-wide via
+ * {@link ConnectivityManager#setProcessDefaultNetwork}.
*/
public class Network implements Parcelable {
@@ -160,63 +162,13 @@
* @return a {@link SocketFactory} which produces {@link Socket} instances bound to this
* {@code Network}.
*/
- public SocketFactory socketFactory() {
+ public SocketFactory getSocketFactory() {
if (mNetworkBoundSocketFactory == null) {
mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
}
return mNetworkBoundSocketFactory;
}
- /**
- * Binds the current process to this network. All sockets created in the future (and not
- * explicitly bound via a bound {@link SocketFactory} (see {@link Network#socketFactory})
- * will be bound to this network. Note that if this {@code Network} ever disconnects
- * all sockets created in this way will cease to work. This is by design so an application
- * doesn't accidentally use sockets it thinks are still bound to a particular {@code Network}.
- */
- public void bindProcess() {
- NetworkUtils.bindProcessToNetwork(netId);
- }
-
- /**
- * Binds host resolutions performed by this process to this network. {@link #bindProcess}
- * takes precedence over this setting.
- *
- * @hide
- * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
- */
- public void bindProcessForHostResolution() {
- NetworkUtils.bindProcessToNetworkForHostResolution(netId);
- }
-
- /**
- * Clears any process specific {@link Network} binding for host resolution. This does
- * not clear bindings enacted via {@link #bindProcess}.
- *
- * @hide
- * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
- */
- public void unbindProcessForHostResolution() {
- NetworkUtils.unbindProcessToNetworkForHostResolution();
- }
-
- /**
- * A static utility method to return any {@code Network} currently bound by this process.
- *
- * @return {@code Network} to which this process is bound.
- */
- public static Network getProcessBoundNetwork() {
- return new Network(NetworkUtils.getNetworkBoundToProcess());
- }
-
- /**
- * Clear any process specific {@code Network} binding. This reverts a call to
- * {@link Network#bindProcess}.
- */
- public static void unbindProcess() {
- NetworkUtils.unbindProcessToNetwork();
- }
-
// implement the Parcelable interface
public int describeContents() {
return 0;
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 7e8b1f1..3d0874b 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -80,6 +80,11 @@
*/
public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
+ /* centralize place where base network score, and network score scaling, will be
+ * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
+ */
+ public static final int WIFI_BASE_SCORE = 60;
+
/**
* Sent by the NetworkAgent to ConnectivityService to pass the current
* network score.
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 35274f1..fe96287 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -44,6 +44,9 @@
private static final String TAG = "NetworkCapabilities";
private static final boolean DBG = false;
+ /**
+ * @hide
+ */
public NetworkCapabilities() {
}
@@ -154,58 +157,64 @@
* Multiple capabilities may be applied sequentially. Note that when searching
* for a network to satisfy a request, all capabilities requested must be satisfied.
*
- * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
+ * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
+ * @return This NetworkCapability to facilitate chaining.
+ * @hide
*/
- public void addNetworkCapability(int networkCapability) {
- if (networkCapability < MIN_NET_CAPABILITY ||
- networkCapability > MAX_NET_CAPABILITY) {
+ public NetworkCapabilities addCapability(int capability) {
+ if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
throw new IllegalArgumentException("NetworkCapability out of range");
}
- mNetworkCapabilities |= 1 << networkCapability;
+ mNetworkCapabilities |= 1 << capability;
+ return this;
}
/**
* Removes (if found) the given capability from this {@code NetworkCapability} instance.
*
- * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed.
+ * @param capability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed.
+ * @return This NetworkCapability to facilitate chaining.
+ * @hide
*/
- public void removeNetworkCapability(int networkCapability) {
- if (networkCapability < MIN_NET_CAPABILITY ||
- networkCapability > MAX_NET_CAPABILITY) {
+ public NetworkCapabilities removeCapability(int capability) {
+ if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
throw new IllegalArgumentException("NetworkCapability out of range");
}
- mNetworkCapabilities &= ~(1 << networkCapability);
+ mNetworkCapabilities &= ~(1 << capability);
+ return this;
}
/**
* Gets all the capabilities set on this {@code NetworkCapability} instance.
*
- * @return a {@link Collection} of {@code NetworkCapabilities.NET_CAPABILITY_*} values
+ * @return an array of {@code NetworkCapabilities.NET_CAPABILITY_*} values
* for this instance.
+ * @hide
*/
- public Collection<Integer> getNetworkCapabilities() {
+ public int[] getCapabilities() {
return enumerateBits(mNetworkCapabilities);
}
/**
* Tests for the presence of a capabilitity on this instance.
*
- * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
+ * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
* @return {@code true} if set on this instance.
*/
- public boolean hasCapability(int networkCapability) {
- if (networkCapability < MIN_NET_CAPABILITY ||
- networkCapability > MAX_NET_CAPABILITY) {
+ public boolean hasCapability(int capability) {
+ if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
return false;
}
- return ((mNetworkCapabilities & (1 << networkCapability)) != 0);
+ return ((mNetworkCapabilities & (1 << capability)) != 0);
}
- private Collection<Integer> enumerateBits(long val) {
- ArrayList<Integer> result = new ArrayList<Integer>();
+ private int[] enumerateBits(long val) {
+ int size = Long.bitCount(val);
+ int[] result = new int[size];
+ int index = 0;
int resource = 0;
while (val > 0) {
- if ((val & 1) == 1) result.add(resource);
+ if ((val & 1) == 1) result[index++] = resource;
val = val >> 1;
resource++;
}
@@ -265,33 +274,40 @@
* {@code NetworkCapabilities.NET_CAPABILITY_*} listed above.
*
* @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be added.
+ * @return This NetworkCapability to facilitate chaining.
+ * @hide
*/
- public void addTransportType(int transportType) {
+ public NetworkCapabilities addTransportType(int transportType) {
if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
throw new IllegalArgumentException("TransportType out of range");
}
mTransportTypes |= 1 << transportType;
+ return this;
}
/**
* Removes (if found) the given transport from this {@code NetworkCapability} instance.
*
* @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be removed.
+ * @return This NetworkCapability to facilitate chaining.
+ * @hide
*/
- public void removeTransportType(int transportType) {
+ public NetworkCapabilities removeTransportType(int transportType) {
if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
throw new IllegalArgumentException("TransportType out of range");
}
mTransportTypes &= ~(1 << transportType);
+ return this;
}
/**
* Gets all the transports set on this {@code NetworkCapability} instance.
*
- * @return a {@link Collection} of {@code NetworkCapabilities.TRANSPORT_*} values
+ * @return an array of {@code NetworkCapabilities.TRANSPORT_*} values
* for this instance.
+ * @hide
*/
- public Collection<Integer> getTransportTypes() {
+ public int[] getTransportTypes() {
return enumerateBits(mTransportTypes);
}
@@ -340,6 +356,7 @@
* fast backhauls and slow backhauls.
*
* @param upKbps the estimated first hop upstream (device to network) bandwidth.
+ * @hide
*/
public void setLinkUpstreamBandwidthKbps(int upKbps) {
mLinkUpBandwidthKbps = upKbps;
@@ -368,6 +385,7 @@
* fast backhauls and slow backhauls.
*
* @param downKbps the estimated first hop downstream (network to device) bandwidth.
+ * @hide
*/
public void setLinkDownstreamBandwidthKbps(int downKbps) {
mLinkDownBandwidthKbps = downKbps;
@@ -464,24 +482,22 @@
};
public String toString() {
- Collection<Integer> types = getTransportTypes();
- String transports = (types.size() > 0 ? " Transports: " : "");
- Iterator<Integer> i = types.iterator();
- while (i.hasNext()) {
- switch (i.next()) {
+ int[] types = getTransportTypes();
+ String transports = (types.length > 0 ? " Transports: " : "");
+ for (int i = 0; i < types.length;) {
+ switch (types[i]) {
case TRANSPORT_CELLULAR: transports += "CELLULAR"; break;
case TRANSPORT_WIFI: transports += "WIFI"; break;
case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break;
case TRANSPORT_ETHERNET: transports += "ETHERNET"; break;
}
- if (i.hasNext()) transports += "|";
+ if (++i < types.length) transports += "|";
}
- types = getNetworkCapabilities();
- String capabilities = (types.size() > 0 ? " Capabilities: " : "");
- i = types.iterator();
- while (i.hasNext()) {
- switch (i.next().intValue()) {
+ types = getCapabilities();
+ String capabilities = (types.length > 0 ? " Capabilities: " : "");
+ for (int i = 0; i < types.length; ) {
+ switch (types[i]) {
case NET_CAPABILITY_MMS: capabilities += "MMS"; break;
case NET_CAPABILITY_SUPL: capabilities += "SUPL"; break;
case NET_CAPABILITY_DUN: capabilities += "DUN"; break;
@@ -497,7 +513,7 @@
case NET_CAPABILITY_INTERNET: capabilities += "INTERNET"; break;
case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
}
- if (i.hasNext()) capabilities += "&";
+ if (++i < types.length) capabilities += "&";
}
String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 47377e9..36dc573 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -22,19 +22,14 @@
import java.util.concurrent.atomic.AtomicInteger;
/**
- * Defines a request for a network, made by calling {@link ConnectivityManager#requestNetwork}
- * or {@link ConnectivityManager#listenForNetwork}.
- *
- * This token records the {@link NetworkCapabilities} used to make the request and identifies
- * the request. It should be used to release the request via
- * {@link ConnectivityManager#releaseNetworkRequest} when the network is no longer desired.
+ * Defines a request for a network, made through {@link NetworkRequest.Builder} and used
+ * to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes
+ * via {@link ConnectivityManager#registerNetworkCallback}.
*/
public class NetworkRequest implements Parcelable {
/**
- * The {@link NetworkCapabilities} that define this request. This should not be modified.
- * The networkCapabilities of the request are set when
- * {@link ConnectivityManager#requestNetwork} is called and the value is presented here
- * as a convenient reminder of what was requested.
+ * The {@link NetworkCapabilities} that define this request.
+ * @hide
*/
public final NetworkCapabilities networkCapabilities;
@@ -71,6 +66,95 @@
this.legacyType = that.legacyType;
}
+ /**
+ * Builder used to create {@link NetworkRequest} objects. Specify the Network features
+ * needed in terms of {@link NetworkCapabilities} features
+ */
+ public static class Builder {
+ private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities();
+
+ /**
+ * Default constructor for Builder.
+ */
+ public Builder() {}
+
+ /**
+ * Build {@link NetworkRequest} give the current set of capabilities.
+ */
+ public NetworkRequest build() {
+ return new NetworkRequest(mNetworkCapabilities, ConnectivityManager.TYPE_NONE,
+ ConnectivityManager.REQUEST_ID_UNSET);
+ }
+
+ /**
+ * Add the given capability requirement to this builder. These represent
+ * the requested network's required capabilities. Note that when searching
+ * for a network to satisfy a request, all capabilities requested must be
+ * satisfied. See {@link NetworkCapabilities} for {@code NET_CAPABILITIY_*}
+ * definitions.
+ *
+ * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to add.
+ * @return The builder to facilitate chaining
+ * {@code builder.addCapability(...).addCapability();}.
+ */
+ public Builder addCapability(int capability) {
+ mNetworkCapabilities.addCapability(capability);
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given capability from this builder instance.
+ *
+ * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to remove.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder removeCapability(int capability) {
+ mNetworkCapabilities.removeCapability(capability);
+ return this;
+ }
+
+ /**
+ * Adds the given transport requirement to this builder. These represent
+ * the set of allowed transports for the request. Only networks using one
+ * of these transports will satisfy the request. If no particular transports
+ * are required, none should be specified here. See {@link NetworkCapabilities}
+ * for {@code TRANSPORT_*} definitions.
+ *
+ * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to add.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder addTransportType(int transportType) {
+ mNetworkCapabilities.addTransportType(transportType);
+ return this;
+ }
+
+ /**
+ * Removes (if found) the given transport from this builder instance.
+ *
+ * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to remove.
+ * @return The builder to facilitate chaining.
+ */
+ public Builder removeTransportType(int transportType) {
+ mNetworkCapabilities.removeTransportType(transportType);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setLinkUpstreamBandwidthKbps(int upKbps) {
+ mNetworkCapabilities.setLinkUpstreamBandwidthKbps(upKbps);
+ return this;
+ }
+ /**
+ * @hide
+ */
+ public Builder setLinkDownstreamBandwidthKbps(int downKbps) {
+ mNetworkCapabilities.setLinkDownstreamBandwidthKbps(downKbps);
+ return this;
+ }
+ }
+
// implement the Parcelable interface
public int describeContents() {
return 0;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index edb3286..b02f88e 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -111,7 +111,7 @@
/**
* Binds the current process to the network designated by {@code netId}. All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
- * {@link Network#socketFactory}) will be bound to this network. Note that if this
+ * {@link Network#getSocketFactory}) will be bound to this network. Note that if this
* {@code Network} ever disconnects all sockets created in this way will cease to work. This
* is by design so an application doesn't accidentally use sockets it thinks are still bound to
* a particular {@code Network}.
diff --git a/core/java/android/net/ProxyDataTracker.java b/core/java/android/net/ProxyDataTracker.java
index 4973b3d..a578383 100644
--- a/core/java/android/net/ProxyDataTracker.java
+++ b/core/java/android/net/ProxyDataTracker.java
@@ -48,6 +48,7 @@
// TODO: investigate how to get these DNS addresses from the system.
private static final String DNS1 = "8.8.8.8";
private static final String DNS2 = "8.8.4.4";
+ private static final String INTERFACE_NAME = "ifb0";
private static final String REASON_ENABLED = "enabled";
private static final String REASON_DISABLED = "disabled";
private static final String REASON_PROXY_DOWN = "proxy_down";
@@ -107,10 +108,11 @@
mNetworkCapabilities = new NetworkCapabilities();
mNetworkInfo.setIsAvailable(true);
try {
- mLinkProperties.addDns(InetAddress.getByName(DNS1));
- mLinkProperties.addDns(InetAddress.getByName(DNS2));
+ mLinkProperties.addDnsServer(InetAddress.getByName(DNS1));
+ mLinkProperties.addDnsServer(InetAddress.getByName(DNS2));
+ mLinkProperties.setInterfaceName(INTERFACE_NAME);
} catch (UnknownHostException e) {
- Log.e(TAG, "Could not add DNS address", e);
+ Log.e(TAG, "Could not add DNS address", e);
}
}
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index ad8e4f7..63d6cd3 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -35,10 +35,10 @@
*
* A route contains three pieces of information:
* <ul>
- * <li>a destination {@link LinkAddress} for directly-connected subnets. If this is
- * {@code null} it indicates a default route of the address family (IPv4 or IPv6)
+ * <li>a destination {@link IpPrefix} specifying the network destinations covered by this route.
+ * If this is {@code null} it indicates a default route of the address family (IPv4 or IPv6)
* implied by the gateway IP address.
- * <li>a gateway {@link InetAddress} for default routes. If this is {@code null} it
+ * <li>a gateway {@link InetAddress} indicating the next hop to use. If this is {@code null} it
* indicates a directly-connected route.
* <li>an interface (which may be unspecified).
* </ul>
@@ -46,9 +46,10 @@
* destination and gateway are both specified, they must be of the same address family
* (IPv4 or IPv6).
*/
-public class RouteInfo implements Parcelable {
+public final class RouteInfo implements Parcelable {
/**
* The IP destination address for this route.
+ * TODO: Make this an IpPrefix.
*/
private final LinkAddress mDestination;
@@ -80,6 +81,19 @@
* @param destination the destination prefix
* @param gateway the IP address to route packets through
* @param iface the interface name to send packets on
+ *
+ * TODO: Convert to use IpPrefix.
+ *
+ * @hide
+ */
+ public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+ this(destination == null ? null :
+ new LinkAddress(destination.getAddress(), destination.getPrefixLength()),
+ gateway, iface);
+ }
+
+ /**
+ * @hide
*/
public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
if (destination == null) {
@@ -105,7 +119,7 @@
mHasGateway = (!gateway.isAnyLocalAddress());
mDestination = new LinkAddress(NetworkUtils.getNetworkPart(destination.getAddress(),
- destination.getNetworkPrefixLength()), destination.getNetworkPrefixLength());
+ destination.getPrefixLength()), destination.getPrefixLength());
if ((destination.getAddress() instanceof Inet4Address &&
(gateway instanceof Inet4Address == false)) ||
(destination.getAddress() instanceof Inet6Address &&
@@ -128,8 +142,17 @@
* <p>
* Destination and gateway may not both be null.
*
- * @param destination the destination address and prefix in a {@link LinkAddress}
+ * @param destination the destination address and prefix in an {@link IpPrefix}
* @param gateway the {@link InetAddress} to route packets through
+ *
+ * @hide
+ */
+ public RouteInfo(IpPrefix destination, InetAddress gateway) {
+ this(destination, gateway, null);
+ }
+
+ /**
+ * @hide
*/
public RouteInfo(LinkAddress destination, InetAddress gateway) {
this(destination, gateway, null);
@@ -139,16 +162,27 @@
* Constructs a default {@code RouteInfo} object.
*
* @param gateway the {@link InetAddress} to route packets through
+ *
+ * @hide
*/
public RouteInfo(InetAddress gateway) {
- this(null, gateway, null);
+ this((LinkAddress) null, gateway, null);
}
/**
* Constructs a {@code RouteInfo} object representing a direct connected subnet.
*
- * @param destination the {@link LinkAddress} describing the address and prefix
+ * @param destination the {@link IpPrefix} describing the address and prefix
* length of the subnet.
+ *
+ * @hide
+ */
+ public RouteInfo(IpPrefix destination) {
+ this(destination, null, null);
+ }
+
+ /**
+ * @hide
*/
public RouteInfo(LinkAddress destination) {
this(destination, null, null);
@@ -176,29 +210,37 @@
private boolean isHost() {
return (mDestination.getAddress() instanceof Inet4Address &&
- mDestination.getNetworkPrefixLength() == 32) ||
+ mDestination.getPrefixLength() == 32) ||
(mDestination.getAddress() instanceof Inet6Address &&
- mDestination.getNetworkPrefixLength() == 128);
+ mDestination.getPrefixLength() == 128);
}
private boolean isDefault() {
boolean val = false;
if (mGateway != null) {
if (mGateway instanceof Inet4Address) {
- val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
+ val = (mDestination == null || mDestination.getPrefixLength() == 0);
} else {
- val = (mDestination == null || mDestination.getNetworkPrefixLength() == 0);
+ val = (mDestination == null || mDestination.getPrefixLength() == 0);
}
}
return val;
}
/**
- * Retrieves the destination address and prefix length in the form of a {@link LinkAddress}.
+ * Retrieves the destination address and prefix length in the form of an {@link IpPrefix}.
*
- * @return {@link LinkAddress} specifying the destination. This is never {@code null}.
+ * @return {@link IpPrefix} specifying the destination. This is never {@code null}.
*/
- public LinkAddress getDestination() {
+ public IpPrefix getDestination() {
+ return new IpPrefix(mDestination.getAddress(), mDestination.getPrefixLength());
+ }
+
+ /**
+ * TODO: Convert callers to use IpPrefix and then remove.
+ * @hide
+ */
+ public LinkAddress getDestinationLinkAddress() {
return mDestination;
}
@@ -206,7 +248,7 @@
* Retrieves the gateway or next hop {@link InetAddress} for this route.
*
* @return {@link InetAddress} specifying the gateway or next hop. This may be
- & {@code null} for a directly-connected route."
+ * {@code null} for a directly-connected route."
*/
public InetAddress getGateway() {
return mGateway;
@@ -233,7 +275,8 @@
/**
* Indicates if this route is a host route (ie, matches only a single host address).
*
- * @return {@code true} if the destination has a prefix length of 32/128 for v4/v6.
+ * @return {@code true} if the destination has a prefix length of 32 or 128 for IPv4 or IPv6,
+ * respectively.
* @hide
*/
public boolean isHostRoute() {
@@ -263,7 +306,7 @@
// match the route destination and destination with prefix length
InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
- mDestination.getNetworkPrefixLength());
+ mDestination.getPrefixLength());
return mDestination.getAddress().equals(dstNet);
}
@@ -285,8 +328,8 @@
for (RouteInfo route : routes) {
if (NetworkUtils.addressTypeMatches(route.mDestination.getAddress(), dest)) {
if ((bestRoute != null) &&
- (bestRoute.mDestination.getNetworkPrefixLength() >=
- route.mDestination.getNetworkPrefixLength())) {
+ (bestRoute.mDestination.getPrefixLength() >=
+ route.mDestination.getPrefixLength())) {
continue;
}
if (route.matches(dest)) bestRoute = route;
@@ -295,13 +338,22 @@
return bestRoute;
}
+ /**
+ * Returns a human-readable description of this object.
+ */
public String toString() {
String val = "";
if (mDestination != null) val = mDestination.toString();
- if (mGateway != null) val += " -> " + mGateway.getHostAddress();
+ val += " ->";
+ if (mGateway != null) val += " " + mGateway.getHostAddress();
+ if (mInterface != null) val += " " + mInterface;
return val;
}
+ /**
+ * Compares this RouteInfo object against the specified object and indicates if they are equal.
+ * @return {@code true} if the objects are equal, {@code false} otherwise.
+ */
public boolean equals(Object obj) {
if (this == obj) return true;
@@ -309,11 +361,14 @@
RouteInfo target = (RouteInfo) obj;
- return Objects.equals(mDestination, target.getDestination()) &&
+ return Objects.equals(mDestination, target.getDestinationLinkAddress()) &&
Objects.equals(mGateway, target.getGateway()) &&
Objects.equals(mInterface, target.getInterface());
}
+ /**
+ * Returns a hashcode for this <code>RouteInfo</code> object.
+ */
public int hashCode() {
return (mDestination == null ? 0 : mDestination.hashCode() * 41)
+ (mGateway == null ? 0 :mGateway.hashCode() * 47)
@@ -323,7 +378,6 @@
/**
* Implement the Parcelable interface
- * @hide
*/
public int describeContents() {
return 0;
@@ -331,7 +385,6 @@
/**
* Implement the Parcelable interface
- * @hide
*/
public void writeToParcel(Parcel dest, int flags) {
if (mDestination == null) {
@@ -339,7 +392,7 @@
} else {
dest.writeByte((byte) 1);
dest.writeByteArray(mDestination.getAddress().getAddress());
- dest.writeInt(mDestination.getNetworkPrefixLength());
+ dest.writeInt(mDestination.getPrefixLength());
}
if (mGateway == null) {
@@ -354,7 +407,6 @@
/**
* Implement the Parcelable interface.
- * @hide
*/
public static final Creator<RouteInfo> CREATOR =
new Creator<RouteInfo>() {
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 32050dc..537e993 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -128,29 +128,97 @@
public static final int BATTERY_PLUGGED_ANY =
BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS;
+ /*
+ * Battery property identifiers. These must match the values in
+ * frameworks/native/include/batteryservice/BatteryService.h
+ */
+ /** Battery capacity in microampere-hours, as an integer. */
+ public static final int BATTERY_PROPERTY_CHARGE_COUNTER = 1;
+
+ /**
+ * Instantaneous battery current in microamperes, as an integer. Positive
+ * values indicate net current entering the battery from a charge source,
+ * negative values indicate net current discharging from the battery.
+ */
+ public static final int BATTERY_PROPERTY_CURRENT_NOW = 2;
+
+ /**
+ * Average battery current in microamperes, as an integer. Positive
+ * values indicate net current entering the battery from a charge source,
+ * negative values indicate net current discharging from the battery.
+ * The time period over which the average is computed may depend on the
+ * fuel gauge hardware and its configuration.
+ */
+ public static final int BATTERY_PROPERTY_CURRENT_AVERAGE = 3;
+
+ /**
+ * Remaining battery capacity as an integer percentage of total capacity
+ * (with no fractional part).
+ */
+ public static final int BATTERY_PROPERTY_CAPACITY = 4;
+
+ /**
+ * Battery remaining energy in nanowatt-hours, as a long integer.
+ */
+ public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5;
+
private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar;
/**
- * Return the requested battery property.
+ * Query a battery property from the batteryproperties service.
*
- * @param id identifier from {@link BatteryProperty} of the requested property
- * @return a {@link BatteryProperty} object that returns the property value, or null on error
+ * Returns the requested value, or Long.MIN_VALUE if property not
+ * supported on this system or on other error.
*/
- public BatteryProperty getProperty(int id) throws RemoteException {
+ private long queryProperty(int id) {
+ long ret;
+
if (mBatteryPropertiesRegistrar == null) {
IBinder b = ServiceManager.getService("batteryproperties");
mBatteryPropertiesRegistrar =
IBatteryPropertiesRegistrar.Stub.asInterface(b);
if (mBatteryPropertiesRegistrar == null)
- return null;
+ return Long.MIN_VALUE;
}
- BatteryProperty prop = new BatteryProperty();
- if ((mBatteryPropertiesRegistrar.getProperty(id, prop) == 0) &&
- (prop.getLong() != Long.MIN_VALUE))
- return prop;
- else
- return null;
+ try {
+ BatteryProperty prop = new BatteryProperty();
+
+ if (mBatteryPropertiesRegistrar.getProperty(id, prop) == 0)
+ ret = prop.getLong();
+ else
+ ret = Long.MIN_VALUE;
+ } catch (RemoteException e) {
+ ret = Long.MIN_VALUE;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Return the value of a battery property of integer type. If the
+ * platform does not provide the property queried, this value will
+ * be Integer.MIN_VALUE.
+ *
+ * @param id identifier of the requested property
+ *
+ * @return the property value, or Integer.MIN_VALUE if not supported.
+ */
+ public int getIntProperty(int id) {
+ return (int)queryProperty(id);
+ }
+
+ /**
+ * Return the value of a battery property of long type If the
+ * platform does not provide the property queried, this value will
+ * be Long.MIN_VALUE.
+ *
+ * @param id identifier of the requested property
+ *
+ * @return the property value, or Long.MIN_VALUE if not supported.
+ */
+ public long getLongProperty(int id) {
+ return queryProperty(id);
}
}
diff --git a/core/java/android/os/BatteryProperty.java b/core/java/android/os/BatteryProperty.java
index 27dad4f..84119bd 100644
--- a/core/java/android/os/BatteryProperty.java
+++ b/core/java/android/os/BatteryProperty.java
@@ -20,44 +20,13 @@
/**
* Battery properties that may be queried using
- * {@link BatteryManager#getProperty
* BatteryManager.getProperty()}
*/
+
+/**
+ * @hide
+ */
public class BatteryProperty implements Parcelable {
- /*
- * Battery property identifiers. These must match the values in
- * frameworks/native/include/batteryservice/BatteryService.h
- */
- /** Battery capacity in microampere-hours, as an integer. */
- public static final int CHARGE_COUNTER = 1;
-
- /**
- * Instantaneous battery current in microamperes, as an integer. Positive
- * values indicate net current entering the battery from a charge source,
- * negative values indicate net current discharging from the battery.
- */
- public static final int CURRENT_NOW = 2;
-
- /**
- * Average battery current in microamperes, as an integer. Positive
- * values indicate net current entering the battery from a charge source,
- * negative values indicate net current discharging from the battery.
- * The time period over which the average is computed may depend on the
- * fuel gauge hardware and its configuration.
- */
- public static final int CURRENT_AVERAGE = 3;
-
- /**
- * Remaining battery capacity as an integer percentage of total capacity
- * (with no fractional part).
- */
- public static final int CAPACITY = 4;
-
- /**
- * Battery remaining energy in nanowatt-hours, as a long integer.
- */
- public static final int ENERGY_COUNTER = 5;
-
private long mValueLong;
/**
@@ -68,30 +37,12 @@
}
/**
- * Return the value of a property of integer type previously queried
- * via {@link BatteryManager#getProperty
- * BatteryManager.getProperty()}. If the platform does
- * not provide the property queried, this value will be
- * Integer.MIN_VALUE.
- *
- * @return The queried property value, or Integer.MIN_VALUE if not supported.
- */
- public int getInt() {
- return (int)mValueLong;
- }
-
- /**
- * Return the value of a property of long type previously queried
- * via {@link BatteryManager#getProperty
- * BatteryManager.getProperty()}. If the platform does
- * not provide the property queried, this value will be
- * Long.MIN_VALUE.
- *
- * @return The queried property value, or Long.MIN_VALUE if not supported.
+ * @hide
*/
public long getLong() {
return mValueLong;
}
+
/*
* Parcel read/write code must be kept in sync with
* frameworks/native/services/batteryservice/BatteryProperty.cpp
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index bc57b33..e627d49 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -931,6 +931,14 @@
}
}
+ /**
+ * Don't allow any more batching in to the current history event. This
+ * is called when printing partial histories, so to ensure that the next
+ * history event will go in to a new batch after what was printed in the
+ * last partial history.
+ */
+ public abstract void commitCurrentHistoryBatchLocked();
+
public abstract int getHistoryTotalSize();
public abstract int getHistoryUsedSize();
@@ -3366,6 +3374,7 @@
}
}
if (histStart >= 0) {
+ commitCurrentHistoryBatchLocked();
pw.print(checkin ? "NEXT: " : " NEXT: "); pw.println(lastTime+1);
}
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e84b695..975bfc2 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -292,6 +292,17 @@
}
/**
+ * Returns the config directory for a user. This is for use by system services to store files
+ * relating to the user which should be readable by any app running as that user.
+ *
+ * @hide
+ */
+ public static File getUserConfigDirectory(int userId) {
+ return new File(new File(new File(
+ getDataDirectory(), "misc"), "user"), Integer.toString(userId));
+ }
+
+ /**
* Returns whether the Encrypted File System feature is enabled on the device or not.
* @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code>
* if disabled.
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 61194e9..658180b 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -41,6 +41,7 @@
void goToSleep(long time, int reason, int flags);
void nap(long time);
boolean isInteractive();
+ boolean isPowerSaveMode();
void reboot(boolean confirm, String reason, boolean wait);
void shutdown(boolean confirm, boolean wait);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index cd47099..e77ef95 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -39,9 +39,6 @@
UserInfo getProfileParent(int userHandle);
UserInfo getUserInfo(int userHandle);
boolean isRestricted();
- void setGuestEnabled(boolean enable);
- boolean isGuestEnabled();
- void wipeUser(int userHandle);
int getUserSerialNumber(int userHandle);
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index d5177e8..92e80a5 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.SdkConstant;
import android.content.Context;
import android.util.Log;
@@ -685,6 +686,30 @@
}
/**
+ * Returns true if the device is currently in power save mode. When in this mode,
+ * applications should reduce their functionality in order to conserve battery as
+ * much as possible. You can monitor for changes to this state with
+ * {@link #ACTION_POWER_SAVE_MODE_CHANGED}.
+ *
+ * @return Returns true if currently in low power mode, else false.
+ */
+ public boolean isPowerSaveMode() {
+ try {
+ return mService.isPowerSaveMode();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
+ * This broadcast is only sent to registered receivers.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_POWER_SAVE_MODE_CHANGED
+ = "android.os.action.POWER_SAVE_MODE_CHANGED";
+
+ /**
* A wake lock is a mechanism to indicate that your application needs
* to have the device stay on.
* <p>
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 112ec1d..86c749a 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -156,6 +156,12 @@
public static final int LAST_ISOLATED_UID = 99999;
/**
+ * Defines the gid shared by all applications running under the same profile.
+ * @hide
+ */
+ public static final int SHARED_USER_GID = 9997;
+
+ /**
* First gid for applications to share resources. Used when forward-locking
* is enabled but all UserHandles need to be able to read the resources.
* @hide
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 6e693a4..afbf983 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -16,7 +16,10 @@
package android.os;
+import android.util.SparseArray;
+
import java.io.PrintWriter;
+import java.util.HashMap;
/**
* Representation of a user on the device.
@@ -66,6 +69,8 @@
final int mHandle;
+ private static final SparseArray<UserHandle> userHandles = new SparseArray<UserHandle>();
+
/**
* Checks to see if the user id is the same for the two uids, i.e., they belong to the same
* user.
@@ -124,6 +129,18 @@
return getUserId(Binder.getCallingUid());
}
+ /** @hide */
+ public static final UserHandle getCallingUserHandle() {
+ int userId = getUserId(Binder.getCallingUid());
+ UserHandle userHandle = userHandles.get(userId);
+ // Intentionally not synchronized to save time
+ if (userHandle == null) {
+ userHandle = new UserHandle(userId);
+ userHandles.put(userId, userHandle);
+ }
+ return userHandle;
+ }
+
/**
* Returns the uid that is composed from the userId and the appId.
* @hide
@@ -145,6 +162,14 @@
}
/**
+ * Returns the gid shared between all apps with this userId.
+ * @hide
+ */
+ public static final int getUserGid(int userId) {
+ return getUid(userId, Process.SHARED_USER_GID);
+ }
+
+ /**
* Returns the shared app gid for a given uid or appId.
* @hide
*/
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ee219e3..468cfe0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -25,6 +25,7 @@
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.provider.Settings;
import android.util.Log;
import com.android.internal.R;
@@ -361,6 +362,16 @@
}
/**
+ * Checks if the calling app is running as a guest user.
+ * @return whether the caller is a guest user.
+ * @hide
+ */
+ public boolean isGuestUser() {
+ UserInfo user = getUserInfo(UserHandle.myUserId());
+ return user != null ? user.isGuest() : false;
+ }
+
+ /**
* Return whether the given user is actively running. This means that
* the user is in the "started" state, not "stopped" -- it is currently
* allowed to run code through scheduled alarms, receiving broadcasts,
@@ -481,10 +492,11 @@
}
/**
- * @hide
* Returns whether the current user has been disallowed from performing certain actions
* or setting certain settings.
- * @param restrictionKey the string key representing the restriction
+ *
+ * @param restrictionKey The string key representing the restriction.
+ * @return {@code true} if the current user has the given restriction, {@code false} otherwise.
*/
public boolean hasUserRestriction(String restrictionKey) {
return hasUserRestriction(restrictionKey, Process.myUserHandle());
@@ -549,6 +561,21 @@
}
/**
+ * Creates a guest user and configures it.
+ * @param context an application context
+ * @param name the name to set for the user
+ * @hide
+ */
+ public UserInfo createGuest(Context context, String name) {
+ UserInfo guest = createUser(name, UserInfo.FLAG_GUEST);
+ if (guest != null) {
+ Settings.Secure.putStringForUser(context.getContentResolver(),
+ Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
+ }
+ return guest;
+ }
+
+ /**
* Creates a user with the specified name and options as a profile of another user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
@@ -690,18 +717,47 @@
}
}
+ /**
+ * If the target user is a managed profile of the calling user or the caller
+ * is itself a managed profile, then this returns a drawable to use as a small
+ * icon to include in a view to distinguish it from the original icon.
+ *
+ * @param user The target user.
+ * @return the drawable or null if no drawable is required.
+ * @hide
+ */
+ public Drawable getBadgeForUser(UserHandle user) {
+ UserInfo userInfo = getUserIfProfile(user.getIdentifier());
+ if (userInfo != null && userInfo.isManagedProfile()) {
+ return Resources.getSystem().getDrawable(
+ com.android.internal.R.drawable.ic_corp_badge);
+ }
+ return null;
+ }
+
private int getBadgeResIdForUser(int userHandle) {
// Return the framework-provided badge.
- List<UserInfo> userProfiles = getProfiles(getUserHandle());
- for (UserInfo user : userProfiles) {
- if (user.id == userHandle
- && user.isManagedProfile()) {
- return com.android.internal.R.drawable.ic_corp_badge;
- }
+ UserInfo userInfo = getUserIfProfile(userHandle);
+ if (userInfo != null && userInfo.isManagedProfile()) {
+ return com.android.internal.R.drawable.ic_corp_icon_badge;
}
return 0;
}
+ /**
+ * @return UserInfo for userHandle if it exists and is a profile of the current
+ * user or null.
+ */
+ private UserInfo getUserIfProfile(int userHandle) {
+ List<UserInfo> userProfiles = getProfiles(getUserHandle());
+ for (UserInfo user : userProfiles) {
+ if (user.id == userHandle) {
+ return user;
+ }
+ }
+ return null;
+ }
+
private Drawable getMergedDrawable(Drawable icon, Drawable badge) {
final int width = icon.getIntrinsicWidth();
final int height = icon.getIntrinsicHeight();
@@ -797,50 +853,6 @@
}
/**
- * Enable or disable the use of a guest account. If disabled, the existing guest account
- * will be wiped.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
- * @param enable whether to enable a guest account.
- * @hide
- */
- public void setGuestEnabled(boolean enable) {
- try {
- mService.setGuestEnabled(enable);
- } catch (RemoteException re) {
- Log.w(TAG, "Could not change guest account availability to " + enable);
- }
- }
-
- /**
- * Checks if a guest user is enabled for this device.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
- * @return whether a guest user is enabled
- * @hide
- */
- public boolean isGuestEnabled() {
- try {
- return mService.isGuestEnabled();
- } catch (RemoteException re) {
- Log.w(TAG, "Could not retrieve guest enabled state");
- return false;
- }
- }
-
- /**
- * Wipes all the data for a user, but doesn't remove the user.
- * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
- * @param userHandle
- * @hide
- */
- public void wipeUser(int userHandle) {
- try {
- mService.wipeUser(userHandle);
- } catch (RemoteException re) {
- Log.w(TAG, "Could not wipe user " + userHandle);
- }
- }
-
- /**
* Returns the maximum number of users that can be created on this device. A return value
* of 1 means that it is a single user device.
* @hide
@@ -870,7 +882,9 @@
++switchableUserCount;
}
}
- return switchableUserCount > 1;
+ final boolean guestEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.GUEST_USER_ENABLED, 0) == 1;
+ return switchableUserCount > 1 || guestEnabled;
}
/**
diff --git a/core/java/android/print/ILayoutResultCallback.aidl b/core/java/android/print/ILayoutResultCallback.aidl
index 43b8c30..68c1dac 100644
--- a/core/java/android/print/ILayoutResultCallback.aidl
+++ b/core/java/android/print/ILayoutResultCallback.aidl
@@ -16,6 +16,7 @@
package android.print;
+import android.os.ICancellationSignal;
import android.print.PrintDocumentInfo;
/**
@@ -24,6 +25,8 @@
* @hide
*/
oneway interface ILayoutResultCallback {
+ void onLayoutStarted(ICancellationSignal cancellation, int sequence);
void onLayoutFinished(in PrintDocumentInfo info, boolean changed, int sequence);
void onLayoutFailed(CharSequence error, int sequence);
+ void onLayoutCanceled(int sequence);
}
diff --git a/core/java/android/print/IPrintDocumentAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl
index 2b95c12..9d384fb 100644
--- a/core/java/android/print/IPrintDocumentAdapter.aidl
+++ b/core/java/android/print/IPrintDocumentAdapter.aidl
@@ -37,5 +37,4 @@
void write(in PageRange[] pages, in ParcelFileDescriptor fd,
IWriteResultCallback callback, int sequence);
void finish();
- void cancel();
}
diff --git a/core/java/android/print/IWriteResultCallback.aidl b/core/java/android/print/IWriteResultCallback.aidl
index 8281c4e..8fb33e1 100644
--- a/core/java/android/print/IWriteResultCallback.aidl
+++ b/core/java/android/print/IWriteResultCallback.aidl
@@ -16,6 +16,7 @@
package android.print;
+import android.os.ICancellationSignal;
import android.print.PageRange;
/**
@@ -24,6 +25,8 @@
* @hide
*/
oneway interface IWriteResultCallback {
+ void onWriteStarted(ICancellationSignal cancellation, int sequence);
void onWriteFinished(in PageRange[] pages, int sequence);
void onWriteFailed(CharSequence error, int sequence);
+ void onWriteCanceled(int sequence);
}
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index c6254e0..2810d55 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -151,6 +151,105 @@
mColorMode = colorMode;
}
+ /**
+ * Gets whether this print attributes are in portrait orientation,
+ * which is the media size is in portrait and all orientation dependent
+ * attributes such as resolution and margins are properly adjusted.
+ *
+ * @return Whether this print attributes are in portrait.
+ *
+ * @hide
+ */
+ public boolean isPortrait() {
+ return mMediaSize.isPortrait();
+ }
+
+ /**
+ * Gets a new print attributes instance which is in portrait orientation,
+ * which is the media size is in portrait and all orientation dependent
+ * attributes such as resolution and margins are properly adjusted.
+ *
+ * @return New instance in portrait orientation if this one is in
+ * landscape, otherwise this instance.
+ *
+ * @hide
+ */
+ public PrintAttributes asPortrait() {
+ if (isPortrait()) {
+ return this;
+ }
+
+ PrintAttributes attributes = new PrintAttributes();
+
+ // Rotate the media size.
+ attributes.setMediaSize(getMediaSize().asPortrait());
+
+ // Rotate the resolution.
+ Resolution oldResolution = getResolution();
+ Resolution newResolution = new Resolution(
+ oldResolution.getId(),
+ oldResolution.getLabel(),
+ oldResolution.getVerticalDpi(),
+ oldResolution.getHorizontalDpi());
+ attributes.setResolution(newResolution);
+
+ // Rotate the physical margins.
+ Margins oldMinMargins = getMinMargins();
+ Margins newMinMargins = new Margins(
+ oldMinMargins.getBottomMils(),
+ oldMinMargins.getLeftMils(),
+ oldMinMargins.getTopMils(),
+ oldMinMargins.getRightMils());
+ attributes.setMinMargins(newMinMargins);
+
+ attributes.setColorMode(getColorMode());
+
+ return attributes;
+ }
+
+ /**
+ * Gets a new print attributes instance which is in landscape orientation,
+ * which is the media size is in landscape and all orientation dependent
+ * attributes such as resolution and margins are properly adjusted.
+ *
+ * @return New instance in landscape orientation if this one is in
+ * portrait, otherwise this instance.
+ *
+ * @hide
+ */
+ public PrintAttributes asLandscape() {
+ if (!isPortrait()) {
+ return this;
+ }
+
+ PrintAttributes attributes = new PrintAttributes();
+
+ // Rotate the media size.
+ attributes.setMediaSize(getMediaSize().asLandscape());
+
+ // Rotate the resolution.
+ Resolution oldResolution = getResolution();
+ Resolution newResolution = new Resolution(
+ oldResolution.getId(),
+ oldResolution.getLabel(),
+ oldResolution.getVerticalDpi(),
+ oldResolution.getHorizontalDpi());
+ attributes.setResolution(newResolution);
+
+ // Rotate the physical margins.
+ Margins oldMinMargins = getMinMargins();
+ Margins newMargins = new Margins(
+ oldMinMargins.getTopMils(),
+ oldMinMargins.getRightMils(),
+ oldMinMargins.getBottomMils(),
+ oldMinMargins.getLeftMils());
+ attributes.setMinMargins(newMargins);
+
+ attributes.setColorMode(getColorMode());
+
+ return attributes;
+ }
+
@Override
public void writeToParcel(Parcel parcel, int flags) {
if (mMediaSize != null) {
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 811751d..9361286 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -24,6 +24,7 @@
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -41,6 +42,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -50,12 +52,12 @@
* <p>
* To obtain a handle to the print manager do the following:
* </p>
- *
+ *
* <pre>
* PrintManager printManager =
* (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
* </pre>
- *
+ *
* <h3>Print mechanics</h3>
* <p>
* The key idea behind printing on the platform is that the content to be printed
@@ -344,7 +346,7 @@
try {
mService.cancelPrintJob(printJobId, mAppId, mUserId);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re);
+ Log.e(LOG_TAG, "Error canceling a print job: " + printJobId, re);
}
}
@@ -505,30 +507,17 @@
private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
implements ActivityLifecycleCallbacks {
-
private final Object mLock = new Object();
- private CancellationSignal mLayoutOrWriteCancellation;
+ private Activity mActivity; // Strong reference OK - cleared in destroy
- private Activity mActivity; // Strong reference OK - cleared in finish()
+ private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in destroy
- private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish
+ private Handler mHandler; // Strong reference OK - cleared in destroy
- private Handler mHandler; // Strong reference OK - cleared in finish()
+ private IPrintDocumentAdapterObserver mObserver; // Strong reference OK - cleared in destroy
- private IPrintDocumentAdapterObserver mObserver; // Strong reference OK - cleared in finish
-
- private LayoutSpec mLastLayoutSpec;
-
- private WriteSpec mLastWriteSpec;
-
- private boolean mStartReqeusted;
- private boolean mStarted;
-
- private boolean mFinishRequested;
- private boolean mFinished;
-
- private boolean mDestroyed;
+ private DestroyableCallback mPendingCallback;
public PrintDocumentAdapterDelegate(Activity activity,
PrintDocumentAdapter documentAdapter) {
@@ -542,11 +531,10 @@
public void setObserver(IPrintDocumentAdapterObserver observer) {
final boolean destroyed;
synchronized (mLock) {
- if (!mDestroyed) {
- mObserver = observer;
- }
- destroyed = mDestroyed;
+ mObserver = observer;
+ destroyed = isDestroyedLocked();
}
+
if (destroyed) {
try {
observer.onDestroy();
@@ -559,126 +547,89 @@
@Override
public void start() {
synchronized (mLock) {
- // Started called or finish called or destroyed - nothing to do.
- if (mStartReqeusted || mFinishRequested || mDestroyed) {
- return;
+ // If destroyed the handler is null.
+ if (!isDestroyedLocked()) {
+ mHandler.obtainMessage(MyHandler.MSG_ON_START,
+ mDocumentAdapter).sendToTarget();
}
-
- mStartReqeusted = true;
-
- doPendingWorkLocked();
}
}
@Override
public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
ILayoutResultCallback callback, Bundle metadata, int sequence) {
- final boolean destroyed;
- synchronized (mLock) {
- destroyed = mDestroyed;
- // If start called and not finished called and not destroyed - do some work.
- if (mStartReqeusted && !mFinishRequested && !mDestroyed) {
- // Layout cancels write and overrides layout.
- if (mLastWriteSpec != null) {
- IoUtils.closeQuietly(mLastWriteSpec.fd);
- mLastWriteSpec = null;
- }
- mLastLayoutSpec = new LayoutSpec();
- mLastLayoutSpec.callback = callback;
- mLastLayoutSpec.oldAttributes = oldAttributes;
- mLastLayoutSpec.newAttributes = newAttributes;
- mLastLayoutSpec.metadata = metadata;
- mLastLayoutSpec.sequence = sequence;
-
- // Cancel the previous cancellable operation.When the
- // cancellation completes we will do the pending work.
- if (cancelPreviousCancellableOperationLocked()) {
- return;
- }
-
- doPendingWorkLocked();
- }
+ ICancellationSignal cancellationTransport = CancellationSignal.createTransport();
+ try {
+ callback.onLayoutStarted(cancellationTransport, sequence);
+ } catch (RemoteException re) {
+ // The spooler is dead - can't recover.
+ Log.e(LOG_TAG, "Error notifying for layout start", re);
+ return;
}
- if (destroyed) {
- try {
- callback.onLayoutFailed(null, sequence);
- } catch (RemoteException re) {
- Log.i(LOG_TAG, "Error notifying for cancelled layout", re);
+
+ synchronized (mLock) {
+ // If destroyed the handler is null.
+ if (isDestroyedLocked()) {
+ return;
}
+
+ CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
+ cancellationTransport);
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = mDocumentAdapter;
+ args.arg2 = oldAttributes;
+ args.arg3 = newAttributes;
+ args.arg4 = cancellationSignal;
+ args.arg5 = new MyLayoutResultCallback(callback, sequence);
+ args.arg6 = metadata;
+
+ mHandler.obtainMessage(MyHandler.MSG_ON_LAYOUT, args).sendToTarget();
}
}
@Override
public void write(PageRange[] pages, ParcelFileDescriptor fd,
IWriteResultCallback callback, int sequence) {
- final boolean destroyed;
- synchronized (mLock) {
- destroyed = mDestroyed;
- // If start called and not finished called and not destroyed - do some work.
- if (mStartReqeusted && !mFinishRequested && !mDestroyed) {
- // Write cancels previous writes.
- if (mLastWriteSpec != null) {
- IoUtils.closeQuietly(mLastWriteSpec.fd);
- mLastWriteSpec = null;
- }
- mLastWriteSpec = new WriteSpec();
- mLastWriteSpec.callback = callback;
- mLastWriteSpec.pages = pages;
- mLastWriteSpec.fd = fd;
- mLastWriteSpec.sequence = sequence;
-
- // Cancel the previous cancellable operation.When the
- // cancellation completes we will do the pending work.
- if (cancelPreviousCancellableOperationLocked()) {
- return;
- }
-
- doPendingWorkLocked();
- }
+ ICancellationSignal cancellationTransport = CancellationSignal.createTransport();
+ try {
+ callback.onWriteStarted(cancellationTransport, sequence);
+ } catch (RemoteException re) {
+ // The spooler is dead - can't recover.
+ Log.e(LOG_TAG, "Error notifying for write start", re);
+ return;
}
- if (destroyed) {
- try {
- callback.onWriteFailed(null, sequence);
- } catch (RemoteException re) {
- Log.i(LOG_TAG, "Error notifying for cancelled write", re);
+
+ synchronized (mLock) {
+ // If destroyed the handler is null.
+ if (isDestroyedLocked()) {
+ return;
}
+
+ CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
+ cancellationTransport);
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = mDocumentAdapter;
+ args.arg2 = pages;
+ args.arg3 = fd;
+ args.arg4 = cancellationSignal;
+ args.arg5 = new MyWriteResultCallback(callback, fd, sequence);
+
+ mHandler.obtainMessage(MyHandler.MSG_ON_WRITE, args).sendToTarget();
}
}
@Override
public void finish() {
synchronized (mLock) {
- // Start not called or finish called or destroyed - nothing to do.
- if (!mStartReqeusted || mFinishRequested || mDestroyed) {
- return;
+ // If destroyed the handler is null.
+ if (!isDestroyedLocked()) {
+ mHandler.obtainMessage(MyHandler.MSG_ON_FINISH,
+ mDocumentAdapter).sendToTarget();
}
-
- mFinishRequested = true;
-
- // When the current write or layout complete we
- // will do the pending work.
- if (mLastLayoutSpec != null || mLastWriteSpec != null) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Waiting for current operation");
- }
- return;
- }
-
- doPendingWorkLocked();
- }
- }
-
- @Override
- public void cancel() {
- // Start not called or finish called or destroyed - nothing to do.
- if (!mStartReqeusted || mFinishRequested || mDestroyed) {
- return;
- }
- // Request cancellation of pending work if needed.
- synchronized (mLock) {
- cancelPreviousCancellableOperationLocked();
}
}
@@ -719,20 +670,14 @@
// Note the the spooler has a death recipient that observes if
// this process gets killed so we cover the case of onDestroy not
// being called due to this process being killed to reclaim memory.
- final IPrintDocumentAdapterObserver observer;
+ IPrintDocumentAdapterObserver observer = null;
synchronized (mLock) {
if (activity == mActivity) {
- mDestroyed = true;
observer = mObserver;
- clearLocked();
- } else {
- observer = null;
- activity = null;
+ destroyLocked();
}
}
if (observer != null) {
- activity.getApplication().unregisterActivityLifecycleCallbacks(
- PrintDocumentAdapterDelegate.this);
try {
observer.onDestroy();
} catch (RemoteException re) {
@@ -741,67 +686,39 @@
}
}
- private boolean isFinished() {
- return mDocumentAdapter == null;
+ private boolean isDestroyedLocked() {
+ return (mActivity == null);
}
- private void clearLocked() {
+ private void destroyLocked() {
+ mActivity.getApplication().unregisterActivityLifecycleCallbacks(
+ PrintDocumentAdapterDelegate.this);
mActivity = null;
+
mDocumentAdapter = null;
+
+ // This method is only called from the main thread, so
+ // clearing the messages guarantees that any time a
+ // message is handled we are not in a destroyed state.
+ mHandler.removeMessages(MyHandler.MSG_ON_START);
+ mHandler.removeMessages(MyHandler.MSG_ON_LAYOUT);
+ mHandler.removeMessages(MyHandler.MSG_ON_WRITE);
+ mHandler.removeMessages(MyHandler.MSG_ON_FINISH);
mHandler = null;
- mLayoutOrWriteCancellation = null;
- mLastLayoutSpec = null;
- if (mLastWriteSpec != null) {
- IoUtils.closeQuietly(mLastWriteSpec.fd);
- mLastWriteSpec = null;
+
+ mObserver = null;
+
+ if (mPendingCallback != null) {
+ mPendingCallback.destroy();
+ mPendingCallback = null;
}
}
- private boolean cancelPreviousCancellableOperationLocked() {
- if (mLayoutOrWriteCancellation != null) {
- mLayoutOrWriteCancellation.cancel();
- if (DEBUG) {
- Log.i(LOG_TAG, "Cancelling previous operation");
- }
- return true;
- }
- return false;
- }
-
- private void doPendingWorkLocked() {
- if (mStartReqeusted && !mStarted) {
- mStarted = true;
- mHandler.sendEmptyMessage(MyHandler.MSG_START);
- } else if (mLastLayoutSpec != null) {
- mHandler.sendEmptyMessage(MyHandler.MSG_LAYOUT);
- } else if (mLastWriteSpec != null) {
- mHandler.sendEmptyMessage(MyHandler.MSG_WRITE);
- } else if (mFinishRequested && !mFinished) {
- mFinished = true;
- mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
- }
- }
-
- private class LayoutSpec {
- ILayoutResultCallback callback;
- PrintAttributes oldAttributes;
- PrintAttributes newAttributes;
- Bundle metadata;
- int sequence;
- }
-
- private class WriteSpec {
- IWriteResultCallback callback;
- PageRange[] pages;
- ParcelFileDescriptor fd;
- int sequence;
- }
-
private final class MyHandler extends Handler {
- public static final int MSG_START = 1;
- public static final int MSG_LAYOUT = 2;
- public static final int MSG_WRITE = 3;
- public static final int MSG_FINISH = 4;
+ public static final int MSG_ON_START = 1;
+ public static final int MSG_ON_LAYOUT = 2;
+ public static final int MSG_ON_WRITE = 3;
+ public static final int MSG_ON_FINISH = 4;
public MyHandler(Looper looper) {
super(looper, null, true);
@@ -809,84 +726,71 @@
@Override
public void handleMessage(Message message) {
- if (isFinished()) {
- return;
- }
switch (message.what) {
- case MSG_START: {
- final PrintDocumentAdapter adapter;
- synchronized (mLock) {
- adapter = mDocumentAdapter;
- }
- if (adapter != null) {
- adapter.onStart();
- }
- } break;
-
- case MSG_LAYOUT: {
- final PrintDocumentAdapter adapter;
- final CancellationSignal cancellation;
- final LayoutSpec layoutSpec;
-
- synchronized (mLock) {
- adapter = mDocumentAdapter;
- layoutSpec = mLastLayoutSpec;
- mLastLayoutSpec = null;
- cancellation = new CancellationSignal();
- mLayoutOrWriteCancellation = cancellation;
- }
-
- if (layoutSpec != null && adapter != null) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Performing layout");
- }
- adapter.onLayout(layoutSpec.oldAttributes,
- layoutSpec.newAttributes, cancellation,
- new MyLayoutResultCallback(layoutSpec.callback,
- layoutSpec.sequence), layoutSpec.metadata);
- }
- } break;
-
- case MSG_WRITE: {
- final PrintDocumentAdapter adapter;
- final CancellationSignal cancellation;
- final WriteSpec writeSpec;
-
- synchronized (mLock) {
- adapter = mDocumentAdapter;
- writeSpec = mLastWriteSpec;
- mLastWriteSpec = null;
- cancellation = new CancellationSignal();
- mLayoutOrWriteCancellation = cancellation;
- }
-
- if (writeSpec != null && adapter != null) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Performing write");
- }
- adapter.onWrite(writeSpec.pages, writeSpec.fd,
- cancellation, new MyWriteResultCallback(writeSpec.callback,
- writeSpec.fd, writeSpec.sequence));
- }
- } break;
-
- case MSG_FINISH: {
+ case MSG_ON_START: {
if (DEBUG) {
- Log.i(LOG_TAG, "Performing finish");
+ Log.i(LOG_TAG, "onStart()");
}
- final PrintDocumentAdapter adapter;
- final Activity activity;
+
+ ((PrintDocumentAdapter) message.obj).onStart();
+ } break;
+
+ case MSG_ON_LAYOUT: {
+ SomeArgs args = (SomeArgs) message.obj;
+ PrintDocumentAdapter adapter = (PrintDocumentAdapter) args.arg1;
+ PrintAttributes oldAttributes = (PrintAttributes) args.arg2;
+ PrintAttributes newAttributes = (PrintAttributes) args.arg3;
+ CancellationSignal cancellation = (CancellationSignal) args.arg4;
+ LayoutResultCallback callback = (LayoutResultCallback) args.arg5;
+ Bundle metadata = (Bundle) args.arg6;
+ args.recycle();
+
+ if (DEBUG) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PrintDocumentAdapter#onLayout() {\n");
+ builder.append("\n oldAttributes:").append(oldAttributes);
+ builder.append("\n newAttributes:").append(newAttributes);
+ builder.append("\n preview:").append(metadata.getBoolean(
+ PrintDocumentAdapter.EXTRA_PRINT_PREVIEW));
+ builder.append("\n}");
+ Log.i(LOG_TAG, builder.toString());
+ }
+
+ adapter.onLayout(oldAttributes, newAttributes, cancellation,
+ callback, metadata);
+ } break;
+
+ case MSG_ON_WRITE: {
+ SomeArgs args = (SomeArgs) message.obj;
+ PrintDocumentAdapter adapter = (PrintDocumentAdapter) args.arg1;
+ PageRange[] pages = (PageRange[]) args.arg2;
+ ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg3;
+ CancellationSignal cancellation = (CancellationSignal) args.arg4;
+ WriteResultCallback callback = (WriteResultCallback) args.arg5;
+ args.recycle();
+
+ if (DEBUG) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("PrintDocumentAdapter#onWrite() {\n");
+ builder.append("\n pages:").append(Arrays.toString(pages));
+ builder.append("\n}");
+ Log.i(LOG_TAG, builder.toString());
+ }
+
+ adapter.onWrite(pages, fd, cancellation, callback);
+ } break;
+
+ case MSG_ON_FINISH: {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onFinish()");
+ }
+
+ ((PrintDocumentAdapter) message.obj).onFinish();
+
+ // Done printing, so destroy this instance as it
+ // should not be used anymore.
synchronized (mLock) {
- adapter = mDocumentAdapter;
- activity = mActivity;
- clearLocked();
- }
- if (adapter != null) {
- adapter.onFinish();
- }
- if (activity != null) {
- activity.getApplication().unregisterActivityLifecycleCallbacks(
- PrintDocumentAdapterDelegate.this);
+ destroyLocked();
}
} break;
@@ -898,7 +802,12 @@
}
}
- private final class MyLayoutResultCallback extends LayoutResultCallback {
+ private interface DestroyableCallback {
+ public void destroy();
+ }
+
+ private final class MyLayoutResultCallback extends LayoutResultCallback
+ implements DestroyableCallback {
private ILayoutResultCallback mCallback;
private final int mSequence;
@@ -910,25 +819,31 @@
@Override
public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
- if (info == null) {
- throw new NullPointerException("document info cannot be null");
- }
final ILayoutResultCallback callback;
synchronized (mLock) {
- if (mDestroyed) {
- Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
- + "finish the printing activity before print completion?");
- return;
- }
callback = mCallback;
- clearLocked();
}
- if (callback != null) {
+
+ // If the callback is null we are destroyed.
+ if (callback == null) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion "
+ + "or did you invoke a callback after finish?");
+ return;
+ }
+
+ try {
+ if (info == null) {
+ throw new NullPointerException("document info cannot be null");
+ }
+
try {
callback.onLayoutFinished(info, changed, mSequence);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
}
+ } finally {
+ destroy();
}
}
@@ -936,46 +851,64 @@
public void onLayoutFailed(CharSequence error) {
final ILayoutResultCallback callback;
synchronized (mLock) {
- if (mDestroyed) {
- Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
- + "finish the printing activity before print completion?");
- return;
- }
callback = mCallback;
- clearLocked();
}
- if (callback != null) {
- try {
- callback.onLayoutFailed(error, mSequence);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
- }
+
+ // If the callback is null we are destroyed.
+ if (callback == null) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion "
+ + "or did you invoke a callback after finish?");
+ return;
+ }
+
+ try {
+ callback.onLayoutFailed(error, mSequence);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
+ } finally {
+ destroy();
}
}
@Override
public void onLayoutCancelled() {
+ final ILayoutResultCallback callback;
synchronized (mLock) {
- if (mDestroyed) {
- Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
- + "finish the printing activity before print completion?");
- return;
- }
- clearLocked();
+ callback = mCallback;
+ }
+
+ // If the callback is null we are destroyed.
+ if (callback == null) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion "
+ + "or did you invoke a callback after finish?");
+ return;
+ }
+
+ try {
+ callback.onLayoutCanceled(mSequence);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
+ } finally {
+ destroy();
}
}
- private void clearLocked() {
- mLayoutOrWriteCancellation = null;
- mCallback = null;
- doPendingWorkLocked();
+ @Override
+ public void destroy() {
+ synchronized (mLock) {
+ mCallback = null;
+ mPendingCallback = null;
+ }
}
}
- private final class MyWriteResultCallback extends WriteResultCallback {
+ private final class MyWriteResultCallback extends WriteResultCallback
+ implements DestroyableCallback {
private ParcelFileDescriptor mFd;
- private int mSequence;
private IWriteResultCallback mCallback;
+ private final int mSequence;
public MyWriteResultCallback(IWriteResultCallback callback,
ParcelFileDescriptor fd, int sequence) {
@@ -988,26 +921,32 @@
public void onWriteFinished(PageRange[] pages) {
final IWriteResultCallback callback;
synchronized (mLock) {
- if (mDestroyed) {
- Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
- + "finish the printing activity before print completion?");
- return;
- }
callback = mCallback;
- clearLocked();
}
- if (pages == null) {
- throw new IllegalArgumentException("pages cannot be null");
+
+ // If the callback is null we are destroyed.
+ if (callback == null) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion "
+ + "or did you invoke a callback after finish?");
+ return;
}
- if (pages.length == 0) {
- throw new IllegalArgumentException("pages cannot be empty");
- }
- if (callback != null) {
+
+ try {
+ if (pages == null) {
+ throw new IllegalArgumentException("pages cannot be null");
+ }
+ if (pages.length == 0) {
+ throw new IllegalArgumentException("pages cannot be empty");
+ }
+
try {
callback.onWriteFinished(pages, mSequence);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error calling onWriteFinished", re);
}
+ } finally {
+ destroy();
}
}
@@ -1015,41 +954,58 @@
public void onWriteFailed(CharSequence error) {
final IWriteResultCallback callback;
synchronized (mLock) {
- if (mDestroyed) {
- Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
- + "finish the printing activity before print completion?");
- return;
- }
callback = mCallback;
- clearLocked();
}
- if (callback != null) {
- try {
- callback.onWriteFailed(error, mSequence);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling onWriteFailed", re);
- }
+
+ // If the callback is null we are destroyed.
+ if (callback == null) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion "
+ + "or did you invoke a callback after finish?");
+ return;
+ }
+
+ try {
+ callback.onWriteFailed(error, mSequence);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling onWriteFailed", re);
+ } finally {
+ destroy();
}
}
@Override
public void onWriteCancelled() {
+ final IWriteResultCallback callback;
synchronized (mLock) {
- if (mDestroyed) {
- Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
- + "finish the printing activity before print completion?");
- return;
- }
- clearLocked();
+ callback = mCallback;
+ }
+
+ // If the callback is null we are destroyed.
+ if (callback == null) {
+ Log.e(LOG_TAG, "PrintDocumentAdapter is destroyed. Did you "
+ + "finish the printing activity before print completion "
+ + "or did you invoke a callback after finish?");
+ return;
+ }
+
+ try {
+ callback.onWriteCanceled(mSequence);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling onWriteCanceled", re);
+ } finally {
+ destroy();
}
}
- private void clearLocked() {
- mLayoutOrWriteCancellation = null;
- IoUtils.closeQuietly(mFd);
- mCallback = null;
- mFd = null;
- doPendingWorkLocked();
+ @Override
+ public void destroy() {
+ synchronized (mLock) {
+ IoUtils.closeQuietly(mFd);
+ mCallback = null;
+ mFd = null;
+ mPendingCallback = null;
+ }
}
}
}
diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java
index d32b71b..abb441b 100644
--- a/core/java/android/print/PrinterDiscoverySession.java
+++ b/core/java/android/print/PrinterDiscoverySession.java
@@ -72,9 +72,9 @@
}
}
- public final void startPrinterDisovery(List<PrinterId> priorityList) {
+ public final void startPrinterDiscovery(List<PrinterId> priorityList) {
if (isDestroyed()) {
- Log.w(LOG_TAG, "Ignoring start printers dsicovery - session destroyed");
+ Log.w(LOG_TAG, "Ignoring start printers discovery - session destroyed");
return;
}
if (!mIsPrinterDiscoveryStarted) {
@@ -122,7 +122,7 @@
try {
mPrintManager.stopPrinterStateTracking(printerId, mUserId);
} catch (RemoteException re) {
- Log.e(LOG_TAG, "Error stoping printer state tracking", re);
+ Log.e(LOG_TAG, "Error stopping printer state tracking", re);
}
}
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index eb0ac2e..1557ab0 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -201,9 +201,9 @@
* should build another one using the {@link PrintJobInfo.Builder} class. You
* can specify any standard properties and add advanced, printer specific,
* ones via {@link PrintJobInfo.Builder#putAdvancedOption(String, String)
- * PrintJobInfo.Builder#putAdvancedOption(String, String)} and {@link
+ * PrintJobInfo.Builder.putAdvancedOption(String, String)} and {@link
* PrintJobInfo.Builder#putAdvancedOption(String, int)
- * PrintJobInfo.Builder#putAdvancedOption(String, int)}. The advanced options
+ * PrintJobInfo.Builder.putAdvancedOption(String, int)}. The advanced options
* are not interpreted by the system, they will not be visible to applications,
* and can only be accessed by your print service via {@link
* PrintJob#getAdvancedStringOption(String) PrintJob.getAdvancedStringOption(String)}
@@ -212,14 +212,26 @@
* <p>
* If the advanced print options activity offers changes to the standard print
* options, you can get the current {@link android.print.PrinterInfo} using the
- * "android.intent.extra.print.EXTRA_PRINTER_INFO" extra which will allow you to
- * present the user with UI options supported by the current printer. For example,
- * if the current printer does not support a give media size, you should not
- * offer it in the advanced print options dialog.
+ * {@link #EXTRA_PRINTER_INFO} extra which will allow you to present the user
+ * with UI options supported by the current printer. For example, if the current
+ * printer does not support a given media size, you should not offer it in the
+ * advanced print options UI.
* </p>
+ *
+ * @see #EXTRA_PRINTER_INFO
*/
public static final String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
+ /**
+ * If you declared an optional activity with advanced print options via the
+ * {@link R.attr#advancedPrintOptionsActivity advancedPrintOptionsActivity}
+ * attribute, this extra is used to pass in the currently selected printer's
+ * {@link android.print.PrinterInfo} to your activity allowing you to inspect it.
+ *
+ * @see #EXTRA_PRINT_JOB_INFO
+ */
+ public static final String EXTRA_PRINTER_INFO = "android.intent.extra.print.PRINTER_INFO";
+
private Handler mHandler;
private IPrintServiceClient mClient;
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index a34d9c3..3853003 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -25,6 +25,7 @@
import android.database.DatabaseUtils;
import android.graphics.BitmapFactory;
import android.net.Uri;
+import android.os.Build;
import android.provider.BrowserContract.Bookmarks;
import android.provider.BrowserContract.Combined;
import android.provider.BrowserContract.History;
@@ -155,8 +156,8 @@
* @param title Title for the bookmark. Can be null or empty string.
* @param url Url for the bookmark. Can be null or empty string.
*/
- public static final void saveBookmark(Context c,
- String title,
+ public static final void saveBookmark(Context c,
+ String title,
String url) {
Intent i = new Intent(Intent.ACTION_INSERT, Browser.BOOKMARKS_URI);
i.putExtra("title", title);
@@ -233,10 +234,10 @@
*
* @param cr The ContentResolver used to access the database.
*/
- public static final Cursor getAllBookmarks(ContentResolver cr) throws
+ public static final Cursor getAllBookmarks(ContentResolver cr) throws
IllegalStateException {
return cr.query(Bookmarks.CONTENT_URI,
- new String[] { Bookmarks.URL },
+ new String[] { Bookmarks.URL },
Bookmarks.IS_FOLDER + " = 0", null, null);
}
@@ -397,19 +398,17 @@
// TODO make a single request to the provider to do this in a single transaction
Cursor cursor = null;
try {
-
+
// Select non-bookmark history, ordered by date
cursor = cr.query(History.CONTENT_URI,
new String[] { History._ID, History.URL, History.DATE_LAST_VISITED },
null, null, History.DATE_LAST_VISITED + " ASC");
if (cursor.moveToFirst() && cursor.getCount() >= MAX_HISTORY_COUNT) {
- final WebIconDatabase iconDb = WebIconDatabase.getInstance();
/* eliminate oldest history items */
for (int i = 0; i < TRUNCATE_N_OLDEST; i++) {
cr.delete(ContentUris.withAppendedId(History.CONTENT_URI, cursor.getLong(0)),
- null, null);
- iconDb.releaseIconForPageUrl(cursor.getString(1));
+ null, null);
if (!cursor.moveToNext()) break;
}
}
@@ -469,13 +468,6 @@
cursor = cr.query(History.CONTENT_URI, new String[] { History.URL }, whereClause,
null, null);
if (cursor.moveToFirst()) {
- final WebIconDatabase iconDb = WebIconDatabase.getInstance();
- do {
- // Delete favicons
- // TODO don't release if the URL is bookmarked
- iconDb.releaseIconForPageUrl(cursor.getString(0));
- } while (cursor.moveToNext());
-
cr.delete(History.CONTENT_URI, whereClause, null);
}
} catch (IllegalStateException e) {
@@ -520,7 +512,7 @@
* @param cr The ContentResolver used to access the database.
* @param url url to remove.
*/
- public static final void deleteFromHistory(ContentResolver cr,
+ public static final void deleteFromHistory(ContentResolver cr,
String url) {
cr.delete(History.CONTENT_URI, History.URL + "=?", new String[] { url });
}
@@ -554,7 +546,7 @@
Log.e(LOGTAG, "clearSearches", e);
}
}
-
+
/**
* Request all icons from the database. This call must either be called
* in the main thread or have had Looper.prepare() invoked in the calling
@@ -563,12 +555,12 @@
* @param cr The ContentResolver used to access the database.
* @param where Clause to be used to limit the query from the database.
* Must be an allowable string to be passed into a database query.
- * @param listener IconListener that gets the icons once they are
+ * @param listener IconListener that gets the icons once they are
* retrieved.
*/
public static final void requestAllIcons(ContentResolver cr, String where,
WebIconDatabase.IconListener listener) {
- WebIconDatabase.getInstance().bulkRequestIconForPageUrl(cr, where, listener);
+ // Do nothing: this is no longer used.
}
/**
diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java
index 9e2aacd..d4c5cfb 100644
--- a/core/java/android/provider/Contacts.java
+++ b/core/java/android/provider/Contacts.java
@@ -58,7 +58,7 @@
@Deprecated
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
- /**
+ /**
* Signifies an email address row that is stored in the ContactMethods table
* @deprecated see {@link android.provider.ContactsContract}
*/
@@ -337,7 +337,7 @@
* @deprecated see {@link android.provider.ContactsContract}
*/
@Deprecated
- public static final class People implements BaseColumns, SyncConstValue, PeopleColumns,
+ public static final class People implements BaseColumns, PeopleColumns,
PhonesColumns, PresenceColumns {
/**
* no public constructor since this is a utility class
@@ -790,7 +790,7 @@
*/
@Deprecated
public static final class Groups
- implements BaseColumns, SyncConstValue, GroupsColumns {
+ implements BaseColumns, GroupsColumns {
/**
* no public constructor since this is a utility class
*/
@@ -1864,7 +1864,7 @@
* @deprecated see {@link android.provider.ContactsContract}
*/
@Deprecated
- public static final class Photos implements BaseColumns, PhotosColumns, SyncConstValue {
+ public static final class Photos implements BaseColumns, PhotosColumns {
/**
* no public constructor since this is a utility class
*/
@@ -2199,7 +2199,7 @@
}
/** The action code to use when adding a contact
- * @deprecated see {@link android.provider.ContactsContract}
+ * @deprecated see {@link android.provider.ContactsContract}
*/
@Deprecated
public static final String ACTION = ContactsContract.Intents.Insert.ACTION;
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 6db78f4..ba66e65 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -184,9 +184,9 @@
public static final String DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query";
/**
- * A boolean parameter for {@link CommonDataKinds.Phone#CONTENT_URI},
- * {@link CommonDataKinds.Email#CONTENT_URI}, and
- * {@link CommonDataKinds.StructuredPostal#CONTENT_URI}.
+ * A boolean parameter for {@link CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI},
+ * {@link CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI}, and
+ * {@link CommonDataKinds.StructuredPostal#CONTENT_URI StructuredPostal.CONTENT_URI}.
* This enables a content provider to remove duplicate entries in results.
*/
public static final String REMOVE_DUPLICATE_ENTRIES = "remove_duplicate_entries";
@@ -244,6 +244,9 @@
public static final String KEY_AUTHORIZED_URI = "authorized_uri";
}
+ /*
+ * @hide
+ */
public static final class Preferences {
/**
@@ -808,6 +811,7 @@
* The position at which the contact is pinned. If {@link PinnedPositions#UNPINNED},
* the contact is not pinned. Also see {@link PinnedPositions}.
* <P>Type: INTEGER </P>
+ * @hide
*/
public static final String PINNED = "pinned";
@@ -7775,6 +7779,8 @@
* {@link PinnedPositions#STAR_WHEN_PINNING} to true to force all pinned and unpinned
* contacts to be automatically starred and unstarred.
* </p>
+ *
+ * @hide
*/
public static final class PinnedPositions {
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 6b8e2de..327fe4a 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -60,7 +60,8 @@
* <p>
* All client apps must hold a valid URI permission grant to access documents,
* typically issued when a user makes a selection through
- * {@link Intent#ACTION_OPEN_DOCUMENT} or {@link Intent#ACTION_CREATE_DOCUMENT}.
+ * {@link Intent#ACTION_OPEN_DOCUMENT}, {@link Intent#ACTION_CREATE_DOCUMENT},
+ * or {@link Intent#ACTION_OPEN_DOCUMENT_TREE}.
*
* @see DocumentsProvider
*/
@@ -73,8 +74,8 @@
// content://com.example/root/sdcard/search/?query=pony
// content://com.example/document/12/
// content://com.example/document/12/children/
- // content://com.example/via/12/document/24/
- // content://com.example/via/12/document/24/children/
+ // content://com.example/tree/12/document/24/
+ // content://com.example/tree/12/document/24/children/
private DocumentsContract() {
}
@@ -441,12 +442,13 @@
public static final int FLAG_SUPPORTS_SEARCH = 1 << 3;
/**
- * Flag indicating that this root supports directory selection.
+ * Flag indicating that this root supports testing parent child
+ * relationships.
*
* @see #COLUMN_FLAGS
* @see DocumentsProvider#isChildDocument(String, String)
*/
- public static final int FLAG_SUPPORTS_DIR_SELECTION = 1 << 4;
+ public static final int FLAG_SUPPORTS_IS_CHILD = 1 << 4;
/**
* Flag indicating that this root is currently empty. This may be used
@@ -518,7 +520,7 @@
private static final String PATH_DOCUMENT = "document";
private static final String PATH_CHILDREN = "children";
private static final String PATH_SEARCH = "search";
- private static final String PATH_VIA = "via";
+ private static final String PATH_TREE = "tree";
private static final String PARAM_QUERY = "query";
private static final String PARAM_MANAGE = "manage";
@@ -564,17 +566,17 @@
* Build URI representing access to descendant documents of the given
* {@link Document#COLUMN_DOCUMENT_ID}.
*
- * @see #getViaDocumentId(Uri)
+ * @see #getTreeDocumentId(Uri)
*/
- public static Uri buildViaUri(String authority, String documentId) {
+ public static Uri buildTreeDocumentUri(String authority, String documentId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
- .appendPath(PATH_VIA).appendPath(documentId).build();
+ .appendPath(PATH_TREE).appendPath(documentId).build();
}
/**
- * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a
- * document provider. When queried, a provider will return a single row with
- * columns defined by {@link Document}.
+ * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in
+ * a document provider. When queried, a provider will return a single row
+ * with columns defined by {@link Document}.
*
* @see DocumentsProvider#queryDocument(String, String[])
* @see #getDocumentId(Uri)
@@ -585,42 +587,46 @@
}
/**
- * Build URI representing the given {@link Document#COLUMN_DOCUMENT_ID} in a
- * document provider. Instead of directly accessing the target document,
- * gain access via another document. The target document must be a
- * descendant (child, grandchild, etc) of the via document.
+ * Build URI representing the target {@link Document#COLUMN_DOCUMENT_ID} in
+ * a document provider. When queried, a provider will return a single row
+ * with columns defined by {@link Document}.
+ * <p>
+ * However, instead of directly accessing the target document, the returned
+ * URI will leverage access granted through a subtree URI, typically
+ * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target document
+ * must be a descendant (child, grandchild, etc) of the subtree.
* <p>
* This is typically used to access documents under a user-selected
- * directory, since it doesn't require the user to separately confirm each
- * new document access.
+ * directory tree, since it doesn't require the user to separately confirm
+ * each new document access.
*
- * @param viaUri a related document (directory) that the caller is
- * leveraging to gain access to the target document. The target
- * document must be a descendant of this directory.
+ * @param treeUri the subtree to leverage to gain access to the target
+ * document. The target directory must be a descendant of this
+ * subtree.
* @param documentId the target document, which the caller may not have
* direct access to.
- * @see Intent#ACTION_PICK_DIRECTORY
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
* @see DocumentsProvider#isChildDocument(String, String)
* @see #buildDocumentUri(String, String)
*/
- public static Uri buildDocumentViaUri(Uri viaUri, String documentId) {
+ public static Uri buildDocumentUriUsingTree(Uri treeUri, String documentId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(viaUri.getAuthority()).appendPath(PATH_VIA)
- .appendPath(getViaDocumentId(viaUri)).appendPath(PATH_DOCUMENT)
+ .authority(treeUri.getAuthority()).appendPath(PATH_TREE)
+ .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT)
.appendPath(documentId).build();
}
/** {@hide} */
- public static Uri buildDocumentMaybeViaUri(Uri baseUri, String documentId) {
- if (isViaUri(baseUri)) {
- return buildDocumentViaUri(baseUri, documentId);
+ public static Uri buildDocumentUriMaybeUsingTree(Uri baseUri, String documentId) {
+ if (isTreeUri(baseUri)) {
+ return buildDocumentUriUsingTree(baseUri, documentId);
} else {
return buildDocumentUri(baseUri.getAuthority(), documentId);
}
}
/**
- * Build URI representing the children of the given directory in a document
+ * Build URI representing the children of the target directory in a document
* provider. When queried, a provider will return zero or more rows with
* columns defined by {@link Document}.
*
@@ -637,28 +643,33 @@
}
/**
- * Build URI representing the children of the given directory in a document
- * provider. Instead of directly accessing the target document, gain access
- * via another document. The target document must be a descendant (child,
- * grandchild, etc) of the via document.
+ * Build URI representing the children of the target directory in a document
+ * provider. When queried, a provider will return zero or more rows with
+ * columns defined by {@link Document}.
+ * <p>
+ * However, instead of directly accessing the target directory, the returned
+ * URI will leverage access granted through a subtree URI, typically
+ * returned by {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. The target
+ * directory must be a descendant (child, grandchild, etc) of the subtree.
* <p>
* This is typically used to access documents under a user-selected
- * directory, since it doesn't require the user to separately confirm each
- * new document access.
+ * directory tree, since it doesn't require the user to separately confirm
+ * each new document access.
*
- * @param viaUri a related document (directory) that the caller is
- * leveraging to gain access to the target document. The target
- * document must be a descendant of this directory.
- * @param parentDocumentId the target document, which the caller may not
- * have direct access to.
- * @see Intent#ACTION_PICK_DIRECTORY
+ * @param treeUri the subtree to leverage to gain access to the target
+ * document. The target directory must be a descendant of this
+ * subtree.
+ * @param parentDocumentId the document to return children for, which the
+ * caller may not have direct access to, and which must be a
+ * directory with MIME type of {@link Document#MIME_TYPE_DIR}.
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
* @see DocumentsProvider#isChildDocument(String, String)
* @see #buildChildDocumentsUri(String, String)
*/
- public static Uri buildChildDocumentsViaUri(Uri viaUri, String parentDocumentId) {
+ public static Uri buildChildDocumentsUriUsingTree(Uri treeUri, String parentDocumentId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
- .authority(viaUri.getAuthority()).appendPath(PATH_VIA)
- .appendPath(getViaDocumentId(viaUri)).appendPath(PATH_DOCUMENT)
+ .authority(treeUri.getAuthority()).appendPath(PATH_TREE)
+ .appendPath(getTreeDocumentId(treeUri)).appendPath(PATH_DOCUMENT)
.appendPath(parentDocumentId).appendPath(PATH_CHILDREN).build();
}
@@ -683,21 +694,24 @@
* {@link DocumentsProvider}.
*
* @see #buildDocumentUri(String, String)
- * @see #buildDocumentViaUri(Uri, String)
+ * @see #buildDocumentUriUsingTree(Uri, String)
*/
public static boolean isDocumentUri(Context context, Uri uri) {
final List<String> paths = uri.getPathSegments();
- if (paths.size() >= 2
- && (PATH_DOCUMENT.equals(paths.get(0)) || PATH_VIA.equals(paths.get(0)))) {
+ if (paths.size() == 2 && PATH_DOCUMENT.equals(paths.get(0))) {
+ return isDocumentsProvider(context, uri.getAuthority());
+ }
+ if (paths.size() == 4 && PATH_TREE.equals(paths.get(0))
+ && PATH_DOCUMENT.equals(paths.get(2))) {
return isDocumentsProvider(context, uri.getAuthority());
}
return false;
}
/** {@hide} */
- public static boolean isViaUri(Uri uri) {
+ public static boolean isTreeUri(Uri uri) {
final List<String> paths = uri.getPathSegments();
- return (paths.size() >= 2 && PATH_VIA.equals(paths.get(0)));
+ return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0)));
}
private static boolean isDocumentsProvider(Context context, String authority) {
@@ -733,7 +747,7 @@
if (paths.size() >= 2 && PATH_DOCUMENT.equals(paths.get(0))) {
return paths.get(1);
}
- if (paths.size() >= 4 && PATH_VIA.equals(paths.get(0))
+ if (paths.size() >= 4 && PATH_TREE.equals(paths.get(0))
&& PATH_DOCUMENT.equals(paths.get(2))) {
return paths.get(3);
}
@@ -742,12 +756,10 @@
/**
* Extract the via {@link Document#COLUMN_DOCUMENT_ID} from the given URI.
- *
- * @see #isViaUri(Uri)
*/
- public static String getViaDocumentId(Uri documentUri) {
+ public static String getTreeDocumentId(Uri documentUri) {
final List<String> paths = documentUri.getPathSegments();
- if (paths.size() >= 2 && PATH_VIA.equals(paths.get(0))) {
+ if (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))) {
return paths.get(1);
}
throw new IllegalArgumentException("Invalid URI: " + documentUri);
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 066b4aa..021fff4 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -20,10 +20,14 @@
import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
+import static android.provider.DocumentsContract.buildDocumentUri;
+import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree;
+import static android.provider.DocumentsContract.buildTreeDocumentUri;
import static android.provider.DocumentsContract.getDocumentId;
import static android.provider.DocumentsContract.getRootId;
import static android.provider.DocumentsContract.getSearchDocumentsQuery;
-import static android.provider.DocumentsContract.isViaUri;
+import static android.provider.DocumentsContract.getTreeDocumentId;
+import static android.provider.DocumentsContract.isTreeUri;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -117,6 +121,7 @@
* </p>
*
* @see Intent#ACTION_OPEN_DOCUMENT
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
* @see Intent#ACTION_CREATE_DOCUMENT
*/
public abstract class DocumentsProvider extends ContentProvider {
@@ -128,8 +133,8 @@
private static final int MATCH_SEARCH = 4;
private static final int MATCH_DOCUMENT = 5;
private static final int MATCH_CHILDREN = 6;
- private static final int MATCH_DOCUMENT_VIA = 7;
- private static final int MATCH_CHILDREN_VIA = 8;
+ private static final int MATCH_DOCUMENT_TREE = 7;
+ private static final int MATCH_CHILDREN_TREE = 8;
private String mAuthority;
@@ -149,8 +154,8 @@
mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
- mMatcher.addURI(mAuthority, "via/*/document/*", MATCH_DOCUMENT_VIA);
- mMatcher.addURI(mAuthority, "via/*/document/*/children", MATCH_CHILDREN_VIA);
+ mMatcher.addURI(mAuthority, "tree/*/document/*", MATCH_DOCUMENT_TREE);
+ mMatcher.addURI(mAuthority, "tree/*/document/*/children", MATCH_CHILDREN_TREE);
// Sanity check our setup
if (!info.exported) {
@@ -169,23 +174,24 @@
/**
* Test if a document is descendant (child, grandchild, etc) from the given
- * parent. Providers must override this to support directory selection. You
- * should avoid making network requests to keep this request fast.
+ * parent. For example, providers must implement this to support
+ * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}. You should avoid making network
+ * requests to keep this request fast.
*
* @param parentDocumentId parent to verify against.
* @param documentId child to verify.
* @return if given document is a descendant of the given parent.
- * @see DocumentsContract.Root#FLAG_SUPPORTS_DIR_SELECTION
+ * @see DocumentsContract.Root#FLAG_SUPPORTS_IS_CHILD
*/
public boolean isChildDocument(String parentDocumentId, String documentId) {
return false;
}
/** {@hide} */
- private void enforceVia(Uri documentUri) {
- if (DocumentsContract.isViaUri(documentUri)) {
- final String parent = DocumentsContract.getViaDocumentId(documentUri);
- final String child = DocumentsContract.getDocumentId(documentUri);
+ private void enforceTree(Uri documentUri) {
+ if (isTreeUri(documentUri)) {
+ final String parent = getTreeDocumentId(documentUri);
+ final String child = getDocumentId(documentUri);
if (Objects.equals(parent, child)) {
return;
}
@@ -479,12 +485,12 @@
return querySearchDocuments(
getRootId(uri), getSearchDocumentsQuery(uri), projection);
case MATCH_DOCUMENT:
- case MATCH_DOCUMENT_VIA:
- enforceVia(uri);
+ case MATCH_DOCUMENT_TREE:
+ enforceTree(uri);
return queryDocument(getDocumentId(uri), projection);
case MATCH_CHILDREN:
- case MATCH_CHILDREN_VIA:
- enforceVia(uri);
+ case MATCH_CHILDREN_TREE:
+ enforceTree(uri);
if (DocumentsContract.isManageMode(uri)) {
return queryChildDocumentsForManage(
getDocumentId(uri), projection, sortOrder);
@@ -512,8 +518,8 @@
case MATCH_ROOT:
return DocumentsContract.Root.MIME_TYPE_ITEM;
case MATCH_DOCUMENT:
- case MATCH_DOCUMENT_VIA:
- enforceVia(uri);
+ case MATCH_DOCUMENT_TREE:
+ enforceTree(uri);
return getDocumentType(getDocumentId(uri));
default:
return null;
@@ -530,21 +536,20 @@
* call the superclass. If the superclass returns {@code null}, the subclass
* may implement custom behavior.
* <p>
- * This is typically used to resolve a "via" URI into a concrete document
+ * This is typically used to resolve a subtree URI into a concrete document
* reference, issuing a narrower single-document URI permission grant along
* the way.
*
- * @see DocumentsContract#buildDocumentViaUri(Uri, String)
+ * @see DocumentsContract#buildDocumentUriUsingTree(Uri, String)
*/
@Override
public Uri canonicalize(Uri uri) {
final Context context = getContext();
switch (mMatcher.match(uri)) {
- case MATCH_DOCUMENT_VIA:
- enforceVia(uri);
+ case MATCH_DOCUMENT_TREE:
+ enforceTree(uri);
- final Uri narrowUri = DocumentsContract.buildDocumentUri(uri.getAuthority(),
- DocumentsContract.getDocumentId(uri));
+ final Uri narrowUri = buildDocumentUri(uri.getAuthority(), getDocumentId(uri));
// Caller may only have prefix grant, so extend them a grant to
// the narrow URI.
@@ -628,7 +633,7 @@
throw new SecurityException(
"Requested authority " + authority + " doesn't match provider " + mAuthority);
}
- enforceVia(documentUri);
+ enforceTree(documentUri);
final Bundle out = new Bundle();
try {
@@ -641,8 +646,8 @@
// No need to issue new grants here, since caller either has
// manage permission or a prefix grant. We might generate a
- // "via" style URI if that's how they called us.
- final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri(documentUri,
+ // tree style URI if that's how they called us.
+ final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
newDocumentId);
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
@@ -653,12 +658,12 @@
final String newDocumentId = renameDocument(documentId, displayName);
if (newDocumentId != null) {
- final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri(
- documentUri, newDocumentId);
+ final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
+ newDocumentId);
// If caller came in with a narrow grant, issue them a
// narrow grant for the newly renamed document.
- if (!isViaUri(newDocumentUri)) {
+ if (!isTreeUri(newDocumentUri)) {
final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
documentUri);
context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
@@ -694,8 +699,8 @@
*/
public final void revokeDocumentPermission(String documentId) {
final Context context = getContext();
- context.revokeUriPermission(DocumentsContract.buildDocumentUri(mAuthority, documentId), ~0);
- context.revokeUriPermission(DocumentsContract.buildViaUri(mAuthority, documentId), ~0);
+ context.revokeUriPermission(buildDocumentUri(mAuthority, documentId), ~0);
+ context.revokeUriPermission(buildTreeDocumentUri(mAuthority, documentId), ~0);
}
/**
@@ -705,7 +710,7 @@
*/
@Override
public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- enforceVia(uri);
+ enforceTree(uri);
return openDocument(getDocumentId(uri), mode, null);
}
@@ -717,7 +722,7 @@
@Override
public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
throws FileNotFoundException {
- enforceVia(uri);
+ enforceTree(uri);
return openDocument(getDocumentId(uri), mode, signal);
}
@@ -730,7 +735,7 @@
@SuppressWarnings("resource")
public final AssetFileDescriptor openAssetFile(Uri uri, String mode)
throws FileNotFoundException {
- enforceVia(uri);
+ enforceTree(uri);
final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, null);
return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
}
@@ -744,7 +749,7 @@
@SuppressWarnings("resource")
public final AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal)
throws FileNotFoundException {
- enforceVia(uri);
+ enforceTree(uri);
final ParcelFileDescriptor fd = openDocument(getDocumentId(uri), mode, signal);
return fd != null ? new AssetFileDescriptor(fd, 0, -1) : null;
}
@@ -757,7 +762,7 @@
@Override
public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
throws FileNotFoundException {
- enforceVia(uri);
+ enforceTree(uri);
if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
return openDocumentThumbnail(getDocumentId(uri), sizeHint, null);
@@ -775,7 +780,7 @@
public final AssetFileDescriptor openTypedAssetFile(
Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
throws FileNotFoundException {
- enforceVia(uri);
+ enforceTree(uri);
if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal);
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index cfab1b3..0fe764f 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1886,6 +1886,9 @@
* The MIME type for entries in this table.
*/
public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
+
+ // Not instantiable.
+ private Radio() { }
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bec401e..3fe0fb8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -791,6 +791,15 @@
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_PAIRING_SETTINGS = "android.settings.PAIRING_SETTINGS";
+ /**
+ * Activity Action: Show battery saver settings.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_BATTERY_SAVER_SETTINGS
+ = "android.settings.BATTERY_SAVER_SETTINGS";
+
// End of Intent actions for Settings
/**
@@ -4567,6 +4576,16 @@
public static final String DISPLAY_INTERCEPTED_NOTIFICATIONS = "display_intercepted_notifications";
/**
+ * If enabled, apps should try to skip any introductory hints on first launch. This might
+ * apply to users that are already familiar with the environment or temporary users, like
+ * guests.
+ * <p>
+ * Type : int (0 to show hints, 1 to skip showing hints)
+ * @hide
+ */
+ public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -6024,6 +6043,13 @@
*/
public static final String LOW_POWER_MODE = "low_power";
+ /**
+ * Battery level [1-99] at which low power mode automatically turns on.
+ * If 0, it will not automatically turn on.
+ * @hide
+ */
+ public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
+
/**
* If 1, the activity manager will aggressively finish activities and
* processes as soon as they are no longer needed. If 0, the normal
@@ -6200,6 +6226,14 @@
public static final String DEVICE_NAME = "device_name";
/**
+ * Whether it should be possible to create a guest user on the device.
+ * <p>
+ * Type: int (0 for disabled, 1 for enabled)
+ * @hide
+ */
+ public static final String GUEST_USER_ENABLED = "guest_user_enabled";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/provider/TvContract.java b/core/java/android/provider/TvContract.java
deleted file mode 100644
index 0d90a16..0000000
--- a/core/java/android/provider/TvContract.java
+++ /dev/null
@@ -1,741 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.net.Uri;
-import android.tv.TvInputService;
-
-import java.util.List;
-
-/**
- * <p>
- * The contract between the TV provider and applications. Contains definitions for the supported
- * URIs and columns.
- * </p>
- * <h3>Overview</h3>
- * <p>
- * TvContract defines a basic database of TV content metadata such as channel and program
- * information. The information is stored in {@link Channels} and {@link Programs} tables.
- * </p>
- * <ul>
- * <li>A row in the {@link Channels} table represents information about a TV channel. The data
- * format can vary greatly from standard to standard or according to service provider, thus
- * the columns here are mostly comprised of basic entities that are usually seen to users
- * regardless of standard such as channel number and name.</li>
- * <li>A row in the {@link Programs} table represents a set of data describing a TV program such
- * as program title and start time.</li>
- * </ul>
- */
-public final class TvContract {
- /** The authority for the TV provider. */
- public static final String AUTHORITY = "com.android.tv";
-
- private static final String PATH_CHANNEL = "channel";
- private static final String PATH_PROGRAM = "program";
- private static final String PATH_INPUT = "input";
-
- /**
- * An optional query, update or delete URI parameter that allows the caller to specify start
- * time (in milliseconds since the epoch) to filter programs.
- *
- * @hide
- */
- public static final String PARAM_START_TIME = "start_time";
-
- /**
- * An optional query, update or delete URI parameter that allows the caller to specify end time
- * (in milliseconds since the epoch) to filter programs.
- *
- * @hide
- */
- public static final String PARAM_END_TIME = "end_time";
-
- /**
- * A query, update or delete URI parameter that allows the caller to operate on all or
- * browsable-only channels. If set to "true", the rows that contain non-browsable channels are
- * not affected.
- *
- * @hide
- */
- public static final String PARAM_BROWSABLE_ONLY = "browsable_only";
-
- /**
- * Builds a URI that points to a specific channel.
- *
- * @param channelId The ID of the channel to point to.
- */
- public static final Uri buildChannelUri(long channelId) {
- return ContentUris.withAppendedId(Channels.CONTENT_URI, channelId);
- }
-
- /**
- * Builds a URI that points to all browsable channels from a given TV input.
- *
- * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
- * the given TV input.
- */
- public static final Uri buildChannelsUriForInput(ComponentName name) {
- return buildChannelsUriForInput(name, true);
- }
-
- /**
- * Builds a URI that points to all or browsable-only channels from a given TV input.
- *
- * @param name {@link ComponentName} of the {@link android.tv.TvInputService} that implements
- * the given TV input.
- * @param browsableOnly If set to {@code true} the URI points to only browsable channels. If set
- * to {@code false} the URI points to all channels regardless of whether they are
- * browsable or not.
- */
- public static final Uri buildChannelsUriForInput(ComponentName name, boolean browsableOnly) {
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
- .appendPath(PATH_INPUT).appendPath(name.getPackageName())
- .appendPath(name.getClassName()).appendPath(PATH_CHANNEL)
- .appendQueryParameter(PARAM_BROWSABLE_ONLY, String.valueOf(browsableOnly)).build();
- }
-
- /**
- * Builds a URI that points to a specific program.
- *
- * @param programId The ID of the program to point to.
- */
- public static final Uri buildProgramUri(long programId) {
- return ContentUris.withAppendedId(Programs.CONTENT_URI, programId);
- }
-
- /**
- * Builds a URI that points to all programs on a given channel.
- *
- * @param channelUri The URI of the channel to return programs for.
- */
- public static final Uri buildProgramsUriForChannel(Uri channelUri) {
- if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
- throw new IllegalArgumentException("Not a channel: " + channelUri);
- }
- String channelId = String.valueOf(ContentUris.parseId(channelUri));
- return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
- .appendPath(PATH_CHANNEL).appendPath(channelId).appendPath(PATH_PROGRAM).build();
- }
-
- /**
- * Builds a URI that points to programs on a specific channel whose schedules overlap with the
- * given time frame.
- *
- * @param channelUri The URI of the channel to return programs for.
- * @param startTime The start time used to filter programs. The returned programs should have
- * {@link Programs#COLUMN_END_TIME_UTC_MILLIS} that is greater than this time.
- * @param endTime The end time used to filter programs. The returned programs should have
- * {@link Programs#COLUMN_START_TIME_UTC_MILLIS} that is less than this time.
- */
- public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
- long endTime) {
- Uri uri = buildProgramsUriForChannel(channelUri);
- return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
- .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
- }
-
- /**
- * Builds a URI that points to a specific program the user watched.
- *
- * @param watchedProgramId The ID of the watched program to point to.
- * @hide
- */
- public static final Uri buildWatchedProgramUri(long watchedProgramId) {
- return ContentUris.withAppendedId(WatchedPrograms.CONTENT_URI, watchedProgramId);
- }
-
- /**
- * Extracts the {@link Channels#COLUMN_PACKAGE_NAME} from a given URI.
- *
- * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
- * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
- * @hide
- */
- public static final String getPackageName(Uri channelsUri) {
- final List<String> paths = channelsUri.getPathSegments();
- if (paths.size() < 4) {
- throw new IllegalArgumentException("Not channels: " + channelsUri);
- }
- if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
- throw new IllegalArgumentException("Not channels: " + channelsUri);
- }
- return paths.get(1);
- }
-
- /**
- * Extracts the {@link Channels#COLUMN_SERVICE_NAME} from a given URI.
- *
- * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
- * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
- * @hide
- */
- public static final String getServiceName(Uri channelsUri) {
- final List<String> paths = channelsUri.getPathSegments();
- if (paths.size() < 4) {
- throw new IllegalArgumentException("Not channels: " + channelsUri);
- }
- if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
- throw new IllegalArgumentException("Not channels: " + channelsUri);
- }
- return paths.get(2);
- }
-
- /**
- * Extracts the {@link Channels#_ID} from a given URI.
- *
- * @param programsUri A URI constructed by {@link #buildProgramsUriForChannel(Uri)} or
- * {@link #buildProgramsUriForChannel(Uri, long, long)}.
- * @hide
- */
- public static final String getChannelId(Uri programsUri) {
- final List<String> paths = programsUri.getPathSegments();
- if (paths.size() < 3) {
- throw new IllegalArgumentException("Not programs: " + programsUri);
- }
- if (!PATH_CHANNEL.equals(paths.get(0)) || !PATH_PROGRAM.equals(paths.get(2))) {
- throw new IllegalArgumentException("Not programs: " + programsUri);
- }
- return paths.get(1);
- }
-
-
- private TvContract() {}
-
- /**
- * Common base for the tables of TV channels/programs.
- */
- public interface BaseTvColumns extends BaseColumns {
- /**
- * The name of the package that owns a row in each table.
- * <p>
- * The TV provider fills it in with the name of the package that provides the initial data
- * of that row. If the package is later uninstalled, the rows it owns are automatically
- * removed from the tables.
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_PACKAGE_NAME = "package_name";
- }
-
- /** Column definitions for the TV channels table. */
- public static final class Channels implements BaseTvColumns {
-
- /** The content:// style URI for this table. */
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
- + PATH_CHANNEL);
-
- /** The MIME type of a directory of TV channels. */
- public static final String CONTENT_TYPE =
- "vnd.android.cursor.dir/vnd.com.android.tv.channels";
-
- /** The MIME type of a single TV channel. */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.com.android.tv.channels";
-
- /** A generic channel type. */
- public static final int TYPE_OTHER = 0x0;
-
- /** The special channel type used for pass-through inputs such as HDMI. */
- public static final int TYPE_PASSTHROUGH = 0x00010000;
-
- /** The channel type for DVB-T (terrestrial). */
- public static final int TYPE_DVB_T = 0x00020000;
-
- /** The channel type for DVB-T2 (terrestrial). */
- public static final int TYPE_DVB_T2 = 0x00020001;
-
- /** The channel type for DVB-S (satellite). */
- public static final int TYPE_DVB_S = 0x00020100;
-
- /** The channel type for DVB-S2 (satellite). */
- public static final int TYPE_DVB_S2 = 0x00020101;
-
- /** The channel type for DVB-C (cable). */
- public static final int TYPE_DVB_C = 0x00020200;
-
- /** The channel type for DVB-C2 (cable). */
- public static final int TYPE_DVB_C2 = 0x00020201;
-
- /** The channel type for DVB-H (handheld). */
- public static final int TYPE_DVB_H = 0x00020300;
-
- /** The channel type for DVB-SH (satellite). */
- public static final int TYPE_DVB_SH = 0x00020400;
-
- /** The channel type for ATSC (terrestrial). */
- public static final int TYPE_ATSC_T = 0x00030000;
-
- /** The channel type for ATSC (cable). */
- public static final int TYPE_ATSC_C = 0x00030200;
-
- /** The channel type for ATSC-M/H (mobile/handheld). */
- public static final int TYPE_ATSC_M_H = 0x00030200;
-
- /** The channel type for ISDB-T (terrestrial). */
- public static final int TYPE_ISDB_T = 0x00040000;
-
- /** The channel type for ISDB-Tb (Brazil). */
- public static final int TYPE_ISDB_TB = 0x00040100;
-
- /** The channel type for ISDB-S (satellite). */
- public static final int TYPE_ISDB_S = 0x00040200;
-
- /** The channel type for ISDB-C (cable). */
- public static final int TYPE_ISDB_C = 0x00040300;
-
- /** The channel type for 1seg (handheld). */
- public static final int TYPE_1SEG = 0x00040400;
-
- /** The channel type for DTMB (terrestrial). */
- public static final int TYPE_DTMB = 0x00050000;
-
- /** The channel type for CMMB (handheld). */
- public static final int TYPE_CMMB = 0x00050100;
-
- /** The channel type for T-DMB (terrestrial). */
- public static final int TYPE_T_DMB = 0x00060000;
-
- /** The channel type for S-DMB (satellite). */
- public static final int TYPE_S_DMB = 0x00060100;
-
- /** A generic service type. */
- public static final int SERVICE_TYPE_OTHER = 0x0;
-
- /** The service type for regular TV channels. */
- public static final int SERVICE_TYPE_TV = 0x1;
-
- /** The service type for radio channels. */
- public static final int SERVICE_TYPE_RADIO = 0x2;
-
- /**
- * The name of the {@link TvInputService} subclass that provides this TV channel. This
- * should be a fully qualified class name (such as, "com.example.project.TvInputService").
- * <p>
- * This is a required field.
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_SERVICE_NAME = "service_name";
-
- /**
- * The predefined type of this TV channel.
- * <p>
- * This is primarily used to indicate which broadcast standard (e.g. ATSC, DVB or ISDB) the
- * current channel conforms to, with an exception being {@link #TYPE_PASSTHROUGH}, which is
- * a special channel type used only by pass-through inputs such as HDMI. The value should
- * match to one of the followings: {@link #TYPE_OTHER}, {@link #TYPE_PASSTHROUGH},
- * {@link #TYPE_DVB_T}, {@link #TYPE_DVB_T2}, {@link #TYPE_DVB_S}, {@link #TYPE_DVB_S2},
- * {@link #TYPE_DVB_C}, {@link #TYPE_DVB_C2}, {@link #TYPE_DVB_H}, {@link #TYPE_DVB_SH},
- * {@link #TYPE_ATSC_T}, {@link #TYPE_ATSC_C}, {@link #TYPE_ATSC_M_H}, {@link #TYPE_ISDB_T},
- * {@link #TYPE_ISDB_TB}, {@link #TYPE_ISDB_S}, {@link #TYPE_ISDB_C} {@link #TYPE_1SEG},
- * {@link #TYPE_DTMB}, {@link #TYPE_CMMB}, {@link #TYPE_T_DMB}, {@link #TYPE_S_DMB}
- * </p><p>
- * This is a required field.
- * </p><p>
- * Type: INTEGER
- * </p>
- */
- public static final String COLUMN_TYPE = "type";
-
- /**
- * The predefined service type of this TV channel.
- * <p>
- * This is primarily used to indicate whether the current channel is a regular TV channel or
- * a radio-like channel. Use the same coding for {@code service_type} in the underlying
- * broadcast standard if it is defined there (e.g. ATSC A/53, ETSI EN 300 468 and ARIB
- * STD-B10). Otherwise use one of the followings: {@link #SERVICE_TYPE_OTHER},
- * {@link #SERVICE_TYPE_TV}, {@link #SERVICE_TYPE_RADIO}
- * </p><p>
- * This is a required field.
- * </p><p>
- * Type: INTEGER
- * </p>
- */
- public static final String COLUMN_SERVICE_TYPE = "service_type";
-
- /**
- * The original network ID of this TV channel.
- * <p>
- * This is used to identify the originating delivery system, if applicable. Use the same
- * coding for {@code origianal_network_id} in the underlying broadcast standard if it is
- * defined there (e.g. ETSI EN 300 468/TR 101 211 and ARIB STD-B10). If channels cannot be
- * globally identified by 2-tuple {{@link #COLUMN_TRANSPORT_STREAM_ID},
- * {@link #COLUMN_SERVICE_ID}}, one must carefully assign a value to this field to form a
- * unique 3-tuple identification {{@link #COLUMN_ORIGINAL_NETWORK_ID},
- * {@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}} for its channels.
- * </p><p>
- * This is a required field if the channel cannot be uniquely identified by a 2-tuple
- * {{@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}}.
- * </p><p>
- * Type: INTEGER
- * </p>
- */
- public static final String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
-
- /**
- * The transport stream ID of this channel.
- * <p>
- * This is used to identify the Transport Stream that contains the current channel from any
- * other multiplex within a network, if applicable. Use the same coding for
- * {@code transport_stream_id} defined in ISO/IEC 13818-1 if the channel is transmitted via
- * the MPEG Transport Stream as is the case for many digital broadcast standards.
- * </p><p>
- * This is a required field if the current channel is transmitted via the MPEG Transport
- * Stream.
- * </p><p>
- * Type: INTEGER
- * </p>
- */
- public static final String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
-
- /**
- * The service ID of this channel.
- * <p>
- * This is used to identify the current service (roughly equivalent to channel) from any
- * other service within the Transport Stream, if applicable. Use the same coding for
- * {@code service_id} in the underlying broadcast standard if it is defined there (e.g. ETSI
- * EN 300 468 and ARIB STD-B10) or {@code program_number} (which usually has the same value
- * as {@code service_id}) in ISO/IEC 13818-1 if the channel is transmitted via the MPEG
- * Transport Stream.
- * </p><p>
- * This is a required field if the current channel is transmitted via the MPEG Transport
- * Stream.
- * </p><p>
- * Type: INTEGER
- * </p>
- */
- public static final String COLUMN_SERVICE_ID = "service_id";
-
- /**
- * The channel number that is displayed to the user.
- * <p>
- * The format can vary depending on broadcast standard and product specification.
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_DISPLAY_NUMBER = "display_number";
-
- /**
- * The channel name that is displayed to the user.
- * <p>
- * A call sign is a good candidate to use for this purpose but any name that helps the user
- * recognize the current channel will be enough. Can also be empty depending on broadcast
- * standard.
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_DISPLAY_NAME = "display_name";
-
- /**
- * The description of this TV channel.
- * <p>
- * Can be empty initially.
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_DESCRIPTION = "description";
-
- /**
- * The flag indicating whether this TV channel is browsable or not.
- * <p>
- * A value of 1 indicates the channel is included in the channel list that applications use
- * to browse channels, a value of 0 indicates the channel is not included in the list. If
- * not specified, this value is set to 1 (browsable) by default.
- * </p><p>
- * Type: INTEGER (boolean)
- * </p>
- */
- public static final String COLUMN_BROWSABLE = "browsable";
-
- /**
- * The flag indicating whether this TV channel is searchable or not.
- * <p>
- * In some regions, it is not allowed to surface search results for a given channel without
- * broadcaster's consent. This is used to impose such restriction. A value of 1 indicates
- * the channel is searchable and can be included in search results, a value of 0 indicates
- * the channel and its TV programs are hidden from search. If not specified, this value is
- * set to 1 (searchable) by default.
- * </p>
- * <p>
- * Type: INTEGER (boolean)
- * </p>
- */
- public static final String COLUMN_SEARCHABLE = "searchable";
-
- /**
- * The flag indicating whether this TV channel is locked or not.
- * <p>
- * This is primarily used for alternative parental control to prevent unauthorized users
- * from watching the current channel regardless of the content rating. A value of 1
- * indicates the channel is locked and the user is required to enter passcode to unlock it
- * in order to watch the current program from the channel, a value of 0 indicates the
- * channel is not locked thus the user is not prompted to enter passcode If not specified,
- * this value is set to 0 (not locked) by default.
- * </p><p>
- * Type: INTEGER (boolean)
- * </p>
- */
- public static final String COLUMN_LOCKED = "locked";
-
- /**
- * Generic data used by individual TV input services.
- * <p>
- * Type: BLOB
- * </p>
- */
- public static final String COLUMN_DATA = "data";
-
-
- /**
- * The version number of this row entry used by TV input services.
- * <p>
- * This is best used by sync adapters to identify the rows to update. The number can be
- * defined by individual TV input services. One may assign the same value as
- * {@code version_number} that appears in ETSI EN 300 468 or ATSC A/65, if the data are
- * coming from a TV broadcast.
- * </p><p>
- * Type: INTEGER
- * </p>
- */
- public static final String COLUMN_VERSION_NUMBER = "version_number";
-
- private Channels() {}
- }
-
- /** Column definitions for the TV programs table. */
- public static final class Programs implements BaseTvColumns {
-
- /** The content:// style URI for this table. */
- public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
- + PATH_PROGRAM);
-
- /** The MIME type of a directory of TV programs. */
- public static final String CONTENT_TYPE =
- "vnd.android.cursor.dir/vnd.com.android.tv.programs";
-
- /** The MIME type of a single TV program. */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.com.android.tv.programs";
-
- /**
- * The ID of the TV channel that contains this TV program.
- * <p>
- * This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
- * </p><p>
- * Type: INTEGER (long)
- * </p>
- */
- public static final String COLUMN_CHANNEL_ID = "channel_id";
-
- /**
- * The title of this TV program.
- * <p>
- * Type: TEXT
- * </p>
- **/
- public static final String COLUMN_TITLE = "title";
-
- /**
- * The start time of this TV program, in milliseconds since the epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
- */
- public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
-
- /**
- * The end time of this TV program, in milliseconds since the epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
- */
- public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
-
- /**
- * The comma-separated genre string of this TV program.
- * <p>
- * Use the same language appeared in the underlying broadcast standard, if applicable. (For
- * example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
- * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, use one of the
- * following genres:
- * <ul>
- * <li>Family/Kids</li>
- * <li>Sports</li>
- * <li>Shopping</li>
- * <li>Movies</li>
- * <li>Comedy</li>
- * <li>Travel</li>
- * <li>Drama</li>
- * <li>Education</li>
- * <li>Animal/Wildlife</li>
- * <li>News</li>
- * <li>Gaming</li>
- * <li>Others</li>
- * </ul>
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_GENRE = "genre";
-
- /**
- * The description of this TV program that is displayed to the user by default.
- * <p>
- * The maximum length of this field is 256 characters.
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_DESCRIPTION = "description";
-
- /**
- * The detailed, lengthy description of this TV program that is displayed only when the user
- * wants to see more information.
- * <p>
- * TV input services should leave this field empty if they have no additional
- * details beyond {@link #COLUMN_DESCRIPTION}.
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_LONG_DESCRIPTION = "long_description";
-
- /**
- * The comma-separated audio languages of this TV program.
- * <p>
- * This is used to describe available audio languages included in the program. Use
- * 3-character language code as specified by ISO 639-2.
- * </p><p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
-
- /**
- * Generic data used by TV input services.
- * <p>
- * Type: BLOB
- * </p>
- */
- public static final String COLUMN_DATA = "data";
-
- /**
- * The version number of this row entry used by TV input services.
- * <p>
- * This is best used by sync adapters to identify the rows to update. The number can be
- * defined by individual TV input services. One may assign the same value as
- * {@code version_number} in ETSI EN 300 468 or ATSC A/65, if the data are coming from a TV
- * broadcast.
- * </p><p>
- * Type: INTEGER
- * </p>
- */
- public static final String COLUMN_VERSION_NUMBER = "version_number";
-
- private Programs() {}
- }
-
- /**
- * Column definitions for the TV programs that the user watched. Applications do not have access
- * to this table.
- *
- * @hide
- */
- public static final class WatchedPrograms implements BaseColumns {
-
- /** The content:// style URI for this table. */
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + AUTHORITY + "/watched_program");
-
- /** The MIME type of a directory of watched programs. */
- public static final String CONTENT_TYPE =
- "vnd.android.cursor.dir/vnd.com.android.tv.watched_programs";
-
- /** The MIME type of a single item in this table. */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.com.android.tv.watched_programs";
-
- /**
- * The UTC time that the user started watching this TV program, in milliseconds since the
- * epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
- */
- public static final String COLUMN_WATCH_START_TIME_UTC_MILLIS =
- "watch_start_time_utc_millis";
-
- /**
- * The UTC time that the user stopped watching this TV program, in milliseconds since the
- * epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
- */
- public static final String COLUMN_WATCH_END_TIME_UTC_MILLIS = "watch_end_time_utc_millis";
-
- /**
- * The channel ID that contains this TV program.
- * <p>
- * Type: INTEGER (long)
- * </p>
- */
- public static final String COLUMN_CHANNEL_ID = "channel_id";
-
- /**
- * The title of this TV program.
- * <p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_TITLE = "title";
-
- /**
- * The start time of this TV program, in milliseconds since the epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
- */
- public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
-
- /**
- * The end time of this TV program, in milliseconds since the epoch.
- * <p>
- * Type: INTEGER (long)
- * </p>
- */
- public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
-
- /**
- * The description of this TV program.
- * <p>
- * Type: TEXT
- * </p>
- */
- public static final String COLUMN_DESCRIPTION = "description";
-
- private WatchedPrograms() {}
- }
-}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 557f5a6..8bd0f4d 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -16,7 +16,7 @@
package android.service.notification;
-import android.annotation.PrivateApi;
+import android.annotation.SystemApi;
import android.annotation.SdkConstant;
import android.app.INotificationManager;
import android.app.Service;
@@ -29,6 +29,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.ArrayMap;
import android.util.Log;
import java.util.List;
@@ -54,7 +55,7 @@
+ "[" + getClass().getSimpleName() + "]";
private INotificationListenerWrapper mWrapper = null;
- private Ranking mRanking;
+ private RankingMap mRankingMap;
private INotificationManager mNoMan;
@@ -75,7 +76,22 @@
* object as well as its identifying information (tag and id) and source
* (package name).
*/
- public abstract void onNotificationPosted(StatusBarNotification sbn);
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ // optional
+ }
+
+ /**
+ * Implement this method to learn about new notifications as they are posted by apps.
+ *
+ * @param sbn A data structure encapsulating the original {@link android.app.Notification}
+ * object as well as its identifying information (tag and id) and source
+ * (package name).
+ * @param rankingMap The current ranking map that can be used to retrieve ranking information
+ * for active notifications, including the newly posted one.
+ */
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ onNotificationPosted(sbn);
+ }
/**
* Implement this method to learn when notifications are removed.
@@ -94,7 +110,33 @@
* and source (package name) used to post the {@link android.app.Notification} that
* was just removed.
*/
- public abstract void onNotificationRemoved(StatusBarNotification sbn);
+ public void onNotificationRemoved(StatusBarNotification sbn) {
+ // optional
+ }
+
+ /**
+ * Implement this method to learn when notifications are removed.
+ * <P>
+ * This might occur because the user has dismissed the notification using system UI (or another
+ * notification listener) or because the app has withdrawn the notification.
+ * <P>
+ * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
+ * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
+ * fields such as {@link android.app.Notification#contentView} and
+ * {@link android.app.Notification#largeIcon}. However, all other fields on
+ * {@link StatusBarNotification}, sufficient to match this call with a prior call to
+ * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
+ *
+ * @param sbn A data structure encapsulating at least the original information (tag and id)
+ * and source (package name) used to post the {@link android.app.Notification} that
+ * was just removed.
+ * @param rankingMap The current ranking map that can be used to retrieve ranking information
+ * for active notifications.
+ *
+ */
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+ onNotificationRemoved(sbn);
+ }
/**
* Implement this method to learn about when the listener is enabled and connected to
@@ -107,10 +149,11 @@
/**
* Implement this method to be notified when the notification ranking changes.
- * <P>
- * Call {@link #getCurrentRanking()} to retrieve the new ranking.
+ *
+ * @param rankingMap The current ranking map that can be used to retrieve ranking information
+ * for active notifications.
*/
- public void onNotificationRankingUpdate() {
+ public void onNotificationRankingUpdate(RankingMap rankingMap) {
// optional
}
@@ -241,16 +284,19 @@
*
* <p>
* The returned object represents the current ranking snapshot and only
- * applies for currently active notifications. Hence you must retrieve a
- * new Ranking after each notification event such as
- * {@link #onNotificationPosted(StatusBarNotification)},
- * {@link #onNotificationRemoved(StatusBarNotification)}, etc.
+ * applies for currently active notifications.
+ * <p>
+ * Generally you should use the RankingMap that is passed with events such
+ * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
+ * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and
+ * so on. This method should only be used when needing access outside of
+ * such events, for example to retrieve the RankingMap right after
+ * initialization.
*
- * @return A {@link NotificationListenerService.Ranking} object providing
- * access to ranking information
+ * @return A {@link RankingMap} object providing access to ranking information
*/
- public Ranking getCurrentRanking() {
- return mRanking;
+ public RankingMap getCurrentRanking() {
+ return mRankingMap;
}
@Override
@@ -279,7 +325,7 @@
* @param currentUser the user to use as the stream filter
* @hide
*/
- @PrivateApi
+ @SystemApi
public void registerAsSystemService(ComponentName componentName, int currentUser)
throws RemoteException {
if (mWrapper == null) {
@@ -297,7 +343,7 @@
* with (@link registerAsService).
* @hide
*/
- @PrivateApi
+ @SystemApi
public void unregisterAsSystemService() throws RemoteException {
if (mWrapper != null) {
INotificationManager noMan = getNotificationInterface();
@@ -313,7 +359,7 @@
synchronized (mWrapper) {
applyUpdate(update);
try {
- NotificationListenerService.this.onNotificationPosted(sbn);
+ NotificationListenerService.this.onNotificationPosted(sbn, mRankingMap);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationPosted", t);
}
@@ -326,7 +372,7 @@
synchronized (mWrapper) {
applyUpdate(update);
try {
- NotificationListenerService.this.onNotificationRemoved(sbn);
+ NotificationListenerService.this.onNotificationRemoved(sbn, mRankingMap);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationRemoved", t);
}
@@ -351,7 +397,7 @@
synchronized (mWrapper) {
applyUpdate(update);
try {
- NotificationListenerService.this.onNotificationRankingUpdate();
+ NotificationListenerService.this.onNotificationRankingUpdate(mRankingMap);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationRankingUpdate", t);
}
@@ -360,7 +406,65 @@
}
private void applyUpdate(NotificationRankingUpdate update) {
- mRanking = new Ranking(update);
+ mRankingMap = new RankingMap(update);
+ }
+
+ /**
+ * Provides access to ranking information on a currently active
+ * notification.
+ *
+ * <p>
+ * Note that this object is not updated on notification events (such as
+ * {@link #onNotificationPosted(StatusBarNotification, RankingMap)},
+ * {@link #onNotificationRemoved(StatusBarNotification)}, etc.). Make sure
+ * to retrieve a new Ranking from the current {@link RankingMap} whenever
+ * a notification event occurs.
+ */
+ public static class Ranking {
+ private final String mKey;
+ private final int mRank;
+ private final boolean mIsAmbient;
+ private final boolean mIsInterceptedByDnd;
+
+ private Ranking(String key, int rank, boolean isAmbient, boolean isInterceptedByDnd) {
+ mKey = key;
+ mRank = rank;
+ mIsAmbient = isAmbient;
+ mIsInterceptedByDnd = isInterceptedByDnd;
+ }
+
+ /**
+ * Returns the key of the notification this Ranking applies to.
+ */
+ public String getKey() {
+ return mKey;
+ }
+
+ /**
+ * Returns the rank of the notification.
+ *
+ * @return the rank of the notification, that is the 0-based index in
+ * the list of active notifications.
+ */
+ public int getRank() {
+ return mRank;
+ }
+
+ /**
+ * Returns whether the notification is an ambient notification, that is
+ * a notification that doesn't require the user's immediate attention.
+ */
+ public boolean isAmbient() {
+ return mIsAmbient;
+ }
+
+ /**
+ * Returns whether the notification was intercepted by
+ * "Do not disturb".
+ */
+ public boolean isInterceptedByDoNotDisturb() {
+ return mIsInterceptedByDnd;
+ }
}
/**
@@ -371,11 +475,14 @@
* Note that this object represents a ranking snapshot that only applies to
* notifications active at the time of retrieval.
*/
- public static class Ranking implements Parcelable {
+ public static class RankingMap implements Parcelable {
private final NotificationRankingUpdate mRankingUpdate;
+ private final ArrayMap<String, Ranking> mRankingCache;
+ private boolean mRankingCacheInitialized;
- private Ranking(NotificationRankingUpdate rankingUpdate) {
+ private RankingMap(NotificationRankingUpdate rankingUpdate) {
mRankingUpdate = rankingUpdate;
+ mRankingCache = new ArrayMap<>(rankingUpdate.getOrderedKeys().length);
}
/**
@@ -389,56 +496,37 @@
}
/**
- * Returns the rank of the notification with the given key, that is the
- * index of <code>key</code> in the array of keys returned by
- * {@link #getOrderedKeys()}.
+ * Returns the Ranking for the notification with the given key.
*
- * @return The rank of the notification with the given key; -1 when the
- * given key is unknown.
+ * @return the Ranking of the notification with the given key;
+ * <code>null</code> when the key is unknown.
*/
- public int getRank(String key) {
- // TODO: Optimize.
+ public Ranking getRanking(String key) {
+ synchronized (mRankingCache) {
+ if (!mRankingCacheInitialized) {
+ initializeRankingCache();
+ mRankingCacheInitialized = true;
+ }
+ }
+ return mRankingCache.get(key);
+ }
+
+ private void initializeRankingCache() {
String[] orderedKeys = mRankingUpdate.getOrderedKeys();
- for (int i = 0; i < orderedKeys.length; i++) {
- if (orderedKeys[i].equals(key)) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * Returns whether the notification with the given key was intercepted
- * by "Do not disturb".
- */
- public boolean isInterceptedByDoNotDisturb(String key) {
- // TODO: Optimize.
- for (String interceptedKey : mRankingUpdate.getDndInterceptedKeys()) {
- if (interceptedKey.equals(key)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns whether the notification with the given key is an ambient
- * notification, that is a notification that doesn't require the user's
- * immediate attention.
- */
- public boolean isAmbient(String key) {
- // TODO: Optimize.
int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex();
- if (firstAmbientIndex < 0) {
- return false;
- }
- String[] orderedKeys = mRankingUpdate.getOrderedKeys();
- for (int i = firstAmbientIndex; i < orderedKeys.length; i++) {
- if (orderedKeys[i].equals(key)) {
- return true;
+ for (int i = 0; i < orderedKeys.length; i++) {
+ String key = orderedKeys[i];
+ boolean isAmbient = firstAmbientIndex > -1 && firstAmbientIndex <= i;
+ boolean isInterceptedByDnd = false;
+ // TODO: Optimize.
+ for (String s : mRankingUpdate.getDndInterceptedKeys()) {
+ if (s.equals(key)) {
+ isInterceptedByDnd = true;
+ break;
+ }
}
+ mRankingCache.put(key, new Ranking(key, i, isAmbient, isInterceptedByDnd));
}
- return false;
}
// ----------- Parcelable
@@ -453,16 +541,16 @@
dest.writeParcelable(mRankingUpdate, flags);
}
- public static final Creator<Ranking> CREATOR = new Creator<Ranking>() {
+ public static final Creator<RankingMap> CREATOR = new Creator<RankingMap>() {
@Override
- public Ranking createFromParcel(Parcel source) {
+ public RankingMap createFromParcel(Parcel source) {
NotificationRankingUpdate rankingUpdate = source.readParcelable(null);
- return new Ranking(rankingUpdate);
+ return new RankingMap(rankingUpdate);
}
@Override
- public Ranking[] newArray(int size) {
- return new Ranking[size];
+ public RankingMap[] newArray(int size) {
+ return new RankingMap[size];
}
};
}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index d02fc7b..68a3d30 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -21,6 +21,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.Slog;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -37,6 +38,7 @@
* @hide
*/
public class ZenModeConfig implements Parcelable {
+ private static String TAG = "ZenModeConfig";
public static final String SLEEP_MODE_NIGHTS = "nights";
public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
@@ -65,6 +67,9 @@
private static final String CONDITION_ATT_COMPONENT = "component";
private static final String CONDITION_ATT_ID = "id";
+ private static final String EXIT_CONDITION_TAG = "exitCondition";
+ private static final String EXIT_CONDITION_ATT_ID = "id";
+
public boolean allowCalls;
public boolean allowMessages;
public int allowFrom = SOURCE_ANYONE;
@@ -76,6 +81,7 @@
public int sleepEndMinute;
public ComponentName[] conditionComponents;
public Uri[] conditionIds;
+ public Uri exitConditionId;
public ZenModeConfig() { }
@@ -100,6 +106,7 @@
source.readTypedArray(conditionIds, Uri.CREATOR);
}
allowFrom = source.readInt();
+ exitConditionId = source.readParcelable(null);
}
@Override
@@ -129,6 +136,7 @@
dest.writeInt(0);
}
dest.writeInt(allowFrom);
+ dest.writeParcelable(exitConditionId, 0);
}
@Override
@@ -144,6 +152,7 @@
.append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents))
.append(",conditionIds=")
.append(conditionIds == null ? null : TextUtils.join(",", conditionIds))
+ .append(",exitConditionId=").append(exitConditionId)
.append(']').toString();
}
@@ -174,14 +183,16 @@
&& other.sleepEndHour == sleepEndHour
&& other.sleepEndMinute == sleepEndMinute
&& Objects.deepEquals(other.conditionComponents, conditionComponents)
- && Objects.deepEquals(other.conditionIds, conditionIds);
+ && Objects.deepEquals(other.conditionIds, conditionIds)
+ && Objects.equals(other.exitConditionId, exitConditionId);
}
@Override
public int hashCode() {
return Objects.hash(allowCalls, allowMessages, allowFrom, sleepMode,
sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute,
- Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds));
+ Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds),
+ exitConditionId);
}
public boolean isValid() {
@@ -239,6 +250,8 @@
conditionComponents.add(component);
conditionIds.add(conditionId);
}
+ } else if (EXIT_CONDITION_TAG.equals(tag)) {
+ rt.exitConditionId = safeUri(parser, EXIT_CONDITION_ATT_ID);
}
}
}
@@ -275,6 +288,11 @@
out.endTag(null, CONDITION_TAG);
}
}
+ if (exitConditionId != null) {
+ out.startTag(null, EXIT_CONDITION_TAG);
+ out.attribute(null, EXIT_CONDITION_ATT_ID, exitConditionId.toString());
+ out.endTag(null, EXIT_CONDITION_TAG);
+ }
out.endTag(null, ZEN_TAG);
}
@@ -338,4 +356,33 @@
return new ZenModeConfig[size];
}
};
+
+ // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951
+
+ private static final String COUNTDOWN_AUTHORITY = "android";
+ private static final String COUNTDOWN_PATH = "countdown";
+
+ public static Uri toCountdownConditionId(long time) {
+ return new Uri.Builder().scheme(Condition.SCHEME)
+ .authority(COUNTDOWN_AUTHORITY)
+ .appendPath(COUNTDOWN_PATH)
+ .appendPath(Long.toString(time))
+ .build();
+ }
+
+ public static long tryParseCountdownConditionId(Uri conditionId) {
+ if (!Condition.isValidId(conditionId, COUNTDOWN_AUTHORITY)) return 0;
+ if (conditionId.getPathSegments().size() != 2
+ || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
+ try {
+ return Long.parseLong(conditionId.getPathSegments().get(1));
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
+ return 0;
+ }
+ }
+
+ public static boolean isValidCountdownConditionId(Uri conditionId) {
+ return tryParseCountdownConditionId(conditionId) != 0;
+ }
}
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 98f70f40..a6cddae 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -33,6 +33,8 @@
* A service that notifies the system about whether it believes the environment of the device
* to be trusted.
*
+ * <p>Trust agents may only be provided by the platform.</p>
+ *
* <p>To extend this class, you must declare the service in your manifest file with
* the {@link android.Manifest.permission#BIND_TRUST_AGENT} permission
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
diff --git a/core/java/android/service/voice/DspInfo.java b/core/java/android/service/voice/DspInfo.java
new file mode 100644
index 0000000..0862309
--- /dev/null
+++ b/core/java/android/service/voice/DspInfo.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import java.util.UUID;
+
+/**
+ * Properties of the DSP hardware on the device.
+ * @hide
+ */
+public class DspInfo {
+ /**
+ * Unique voice engine Id (changes with each version).
+ */
+ public final UUID voiceEngineId;
+
+ /**
+ * Human readable voice detection engine implementor.
+ */
+ public final String voiceEngineImplementor;
+ /**
+ * Human readable voice detection engine description.
+ */
+ public final String voiceEngineDescription;
+ /**
+ * Human readable voice detection engine version
+ */
+ public final int voiceEngineVersion;
+ /**
+ * Rated power consumption when detection is active.
+ */
+ public final int powerConsumptionMw;
+
+ public DspInfo(UUID voiceEngineId, String voiceEngineImplementor,
+ String voiceEngineDescription, int version, int powerConsumptionMw) {
+ this.voiceEngineId = voiceEngineId;
+ this.voiceEngineImplementor = voiceEngineImplementor;
+ this.voiceEngineDescription = voiceEngineDescription;
+ this.voiceEngineVersion = version;
+ this.powerConsumptionMw = powerConsumptionMw;
+ }
+}
diff --git a/core/java/android/service/voice/KeyphraseEnrollmentInfo.java b/core/java/android/service/voice/KeyphraseEnrollmentInfo.java
new file mode 100644
index 0000000..ebe41ce
--- /dev/null
+++ b/core/java/android/service/voice/KeyphraseEnrollmentInfo.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.List;
+
+/** @hide */
+public class KeyphraseEnrollmentInfo {
+ private static final String TAG = "KeyphraseEnrollmentInfo";
+ /**
+ * Name under which a Hotword enrollment component publishes information about itself.
+ * This meta-data should reference an XML resource containing a
+ * <code><{@link
+ * android.R.styleable#VoiceEnrollmentApplication
+ * voice-enrollment-application}></code> tag.
+ */
+ private static final String VOICE_KEYPHRASE_META_DATA = "android.voice_enrollment";
+ /**
+ * Activity Action: Show activity for managing the keyphrases for hotword detection.
+ * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase
+ * detection.
+ */
+ public static final String ACTION_MANAGE_VOICE_KEYPHRASES =
+ "com.android.intent.action.MANAGE_VOICE_KEYPHRASES";
+ /**
+ * Intent extra: The intent extra for un-enrolling a user for a particular keyphrase.
+ */
+ public static final String EXTRA_VOICE_KEYPHRASE_UNENROLL =
+ "com.android.intent.extra.VOICE_KEYPHRASE_UNENROLL";
+ /**
+ * Intent extra: The hint text to be shown on the voice keyphrase management UI.
+ */
+ public static final String EXTRA_VOICE_KEYPHRASE_HINT_TEXT =
+ "com.android.intent.extra.VOICE_KEYPHRASE_HINT_TEXT";
+ /**
+ * Intent extra: The voice locale to use while managing the keyphrase.
+ */
+ public static final String EXTRA_VOICE_KEYPHRASE_LOCALE =
+ "com.android.intent.extra.VOICE_KEYPHRASE_LOCALE";
+
+ private KeyphraseInfo[] mKeyphrases;
+ private String mEnrollmentPackage;
+ private String mParseError;
+
+ public KeyphraseEnrollmentInfo(PackageManager pm) {
+ // Find the apps that supports enrollment for hotword keyhphrases,
+ // Pick a privileged app and obtain the information about the supported keyphrases
+ // from its metadata.
+ List<ResolveInfo> ris = pm.queryIntentActivities(
+ new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY);
+ if (ris == null || ris.isEmpty()) {
+ // No application capable of enrolling for voice keyphrases is present.
+ mParseError = "No enrollment application found";
+ return;
+ }
+
+ boolean found = false;
+ ApplicationInfo ai = null;
+ for (ResolveInfo ri : ris) {
+ try {
+ ai = pm.getApplicationInfo(
+ ri.activityInfo.packageName, PackageManager.GET_META_DATA);
+ if ((ai.flags & ApplicationInfo.FLAG_PRIVILEGED) == 0) {
+ // The application isn't privileged (/system/priv-app).
+ // The enrollment application needs to be a privileged system app.
+ Slog.w(TAG, ai.packageName + "is not a privileged system app");
+ continue;
+ }
+ if (!Manifest.permission.MANAGE_VOICE_KEYPHRASES.equals(ai.permission)) {
+ // The application trying to manage keyphrases doesn't
+ // require the MANAGE_VOICE_KEYPHRASES permission.
+ Slog.w(TAG, ai.packageName + " does not require MANAGE_VOICE_KEYPHRASES");
+ continue;
+ }
+ mEnrollmentPackage = ai.packageName;
+ found = true;
+ break;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "error parsing voice enrollment meta-data", e);
+ }
+ }
+
+ if (!found) {
+ mKeyphrases = null;
+ mParseError = "No suitable enrollment application found";
+ return;
+ }
+
+ XmlResourceParser parser = null;
+ try {
+ parser = ai.loadXmlMetaData(pm, VOICE_KEYPHRASE_META_DATA);
+ if (parser == null) {
+ mParseError = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for "
+ + ai.packageName;
+ return;
+ }
+
+ Resources res = pm.getResourcesForApplication(ai);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!"voice-enrollment-application".equals(nodeName)) {
+ mParseError = "Meta-data does not start with voice-enrollment-application tag";
+ return;
+ }
+
+ TypedArray array = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.VoiceEnrollmentApplication);
+ int searchKeyphraseId = array.getInt(
+ com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId,
+ -1);
+ if (searchKeyphraseId != -1) {
+ String searchKeyphrase = array.getString(com.android.internal.R.styleable
+ .VoiceEnrollmentApplication_searchKeyphrase);
+ String searchKeyphraseSupportedLocales =
+ array.getString(com.android.internal.R.styleable
+ .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales);
+ String[] supportedLocales = new String[0];
+ // Get all the supported locales from the comma-delimted string.
+ if (searchKeyphraseSupportedLocales != null
+ && !searchKeyphraseSupportedLocales.isEmpty()) {
+ supportedLocales = searchKeyphraseSupportedLocales.split(",");
+ }
+ mKeyphrases = new KeyphraseInfo[1];
+ mKeyphrases[0] = new KeyphraseInfo(
+ searchKeyphraseId, searchKeyphrase, supportedLocales);
+ } else {
+ mParseError = "searchKeyphraseId not specified in meta-data";
+ return;
+ }
+ } catch (XmlPullParserException e) {
+ mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
+ Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
+ return;
+ } catch (IOException e) {
+ mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
+ Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
+ return;
+ } catch (PackageManager.NameNotFoundException e) {
+ mParseError = "Error parsing keyphrase enrollment meta-data: " + e;
+ Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e);
+ return;
+ } finally {
+ if (parser != null) parser.close();
+ }
+ }
+
+ public String getParseError() {
+ return mParseError;
+ }
+
+ /**
+ * @return An array of available keyphrases that can be enrolled on the system.
+ * It may be null if no keyphrases can be enrolled.
+ */
+ public KeyphraseInfo[] getKeyphrases() {
+ return mKeyphrases;
+ }
+
+ /**
+ * Returns an intent to launch an activity that manages the given keyphrase
+ * for the locale.
+ *
+ * @param enroll Indicates if the intent should enroll the user or un-enroll them.
+ * @param keyphrase The keyphrase that the user needs to be enrolled to.
+ * @param locale The locale for which the enrollment needs to be performed.
+ * @return An {@link Intent} to manage the keyphrase. This can be null if managing the
+ * given keyphrase/locale combination isn't possible.
+ */
+ public Intent getManageKeyphraseIntent(boolean enroll, String keyphrase, String locale) {
+ if (mEnrollmentPackage == null || mEnrollmentPackage.isEmpty()) {
+ Slog.w(TAG, "No enrollment application exists");
+ return null;
+ }
+
+ if (isKeyphraseEnrollmentSupported(keyphrase, locale)) {
+ Intent intent = new Intent(ACTION_MANAGE_VOICE_KEYPHRASES)
+ .setPackage(mEnrollmentPackage)
+ .putExtra(EXTRA_VOICE_KEYPHRASE_HINT_TEXT, keyphrase)
+ .putExtra(EXTRA_VOICE_KEYPHRASE_LOCALE, locale);
+ if (!enroll) intent.putExtra(EXTRA_VOICE_KEYPHRASE_UNENROLL, true);
+ return intent;
+ }
+ return null;
+ }
+
+ /**
+ * Indicates if enrollment is supported for the given keyphrase & locale.
+ *
+ * @param keyphrase The keyphrase that the user needs to be enrolled to.
+ * @param locale The locale for which the enrollment needs to be performed.
+ * @return true, if an enrollment client supports the given keyphrase and the given locale.
+ */
+ public boolean isKeyphraseEnrollmentSupported(String keyphrase, String locale) {
+ if (mKeyphrases == null || mKeyphrases.length == 0) {
+ Slog.w(TAG, "Enrollment application doesn't support keyphrases");
+ return false;
+ }
+ for (KeyphraseInfo keyphraseInfo : mKeyphrases) {
+ // Check if the given keyphrase is supported in the locale provided by
+ // the enrollment application.
+ String supportedKeyphrase = keyphraseInfo.keyphrase;
+ if (supportedKeyphrase.equalsIgnoreCase(keyphrase)
+ && keyphraseInfo.supportedLocales.contains(locale)) {
+ return true;
+ }
+ }
+ Slog.w(TAG, "Enrollment application doesn't support the given keyphrase");
+ return false;
+ }
+}
diff --git a/core/java/android/service/voice/KeyphraseInfo.java b/core/java/android/service/voice/KeyphraseInfo.java
new file mode 100644
index 0000000..d266e1a
--- /dev/null
+++ b/core/java/android/service/voice/KeyphraseInfo.java
@@ -0,0 +1,27 @@
+package android.service.voice;
+
+import android.util.ArraySet;
+
+/**
+ * A Voice Keyphrase.
+ * @hide
+ */
+public class KeyphraseInfo {
+ public final int id;
+ public final String keyphrase;
+ public final ArraySet<String> supportedLocales;
+
+ public KeyphraseInfo(int id, String keyphrase, String[] supportedLocales) {
+ this.id = id;
+ this.keyphrase = keyphrase;
+ this.supportedLocales = new ArraySet<String>(supportedLocales.length);
+ for (String locale : supportedLocales) {
+ this.supportedLocales.add(locale);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "id=" + id + ", keyphrase=" + keyphrase + ", supported-locales=" + supportedLocales;
+ }
+}
diff --git a/core/java/android/service/voice/SoundTriggerManager.java b/core/java/android/service/voice/SoundTriggerManager.java
new file mode 100644
index 0000000..2d049b9
--- /dev/null
+++ b/core/java/android/service/voice/SoundTriggerManager.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
+
+import java.util.ArrayList;
+
+/**
+ * Manager for {@link SoundTrigger} APIs.
+ * Currently this just acts as an abstraction over all SoundTrigger API calls.
+ * @hide
+ */
+public class SoundTriggerManager {
+ /** The {@link DspInfo} for the system, or null if none exists. */
+ public DspInfo dspInfo;
+
+ public SoundTriggerManager() {
+ ArrayList <ModuleProperties> modules = new ArrayList<>();
+ int status = SoundTrigger.listModules(modules);
+ if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
+ // TODO(sansid, elaurent): Figure out how to handle errors in listing the modules here.
+ dspInfo = null;
+ } else {
+ // TODO(sansid, elaurent): Figure out how to determine which module corresponds to the
+ // DSP hardware.
+ ModuleProperties properties = modules.get(0);
+ dspInfo = new DspInfo(properties.uuid, properties.implementor, properties.description,
+ properties.version, properties.powerConsumptionMw);
+ }
+ }
+
+ /**
+ * @return True, if the keyphrase is supported on DSP for the given locale.
+ */
+ public boolean isKeyphraseSupported(String keyphrase, String locale) {
+ // TODO(sansid): We also need to look into a SoundTrigger API that let's us
+ // query this. For now just return supported if there's a DSP available.
+ return dspInfo != null;
+ }
+
+ /**
+ * @return True, if the keyphrase is has been enrolled for the given locale.
+ */
+ public boolean isKeyphraseEnrolled(String keyphrase, String locale) {
+ // TODO(sansid, elaurent): Query SoundTrigger to list currently loaded sound models.
+ // They have been enrolled.
+ return false;
+ }
+
+ /**
+ * @return True, if a recognition for the keyphrase is active for the given locale.
+ */
+ public boolean isKeyphraseActive(String keyphrase, String locale) {
+ // TODO(sansid, elaurent): Check if the recognition for the keyphrase is currently active.
+ return false;
+ }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index e15489b..e0329f8 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -17,7 +17,6 @@
package android.service.voice;
import android.annotation.SdkConstant;
-import android.app.Instrumentation;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -25,8 +24,11 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractionManagerService;
+
/**
* Top-level service of the current global voice interactor, which is providing
* support for hotwording, the back-end of a {@link android.app.VoiceInteractor}, etc.
@@ -51,6 +53,16 @@
public static final String SERVICE_INTERFACE =
"android.service.voice.VoiceInteractionService";
+ // TODO(sansid): Unhide these.
+ /** @hide */
+ public static final int KEYPHRASE_UNAVAILABLE = 0;
+ /** @hide */
+ public static final int KEYPHRASE_UNENROLLED = 1;
+ /** @hide */
+ public static final int KEYPHRASE_ENROLLED = 2;
+ /** @hide */
+ public static final int KEYPHRASE_ACTIVE = 3;
+
/**
* Name under which a VoiceInteractionService component publishes information about itself.
* This meta-data should reference an XML resource containing a
@@ -64,6 +76,9 @@
IVoiceInteractionManagerService mSystemService;
+ private SoundTriggerManager mSoundTriggerManager;
+ private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
+
public void startSession(Bundle args) {
try {
mSystemService.startSession(mInterface, args);
@@ -76,6 +91,8 @@
super.onCreate();
mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+ mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
+ mSoundTriggerManager = new SoundTriggerManager();
}
@Override
@@ -85,4 +102,44 @@
}
return null;
}
+
+ /**
+ * Gets the state of always-on hotword detection for the given keyphrase and locale
+ * on this system.
+ * Availability implies that the hardware on this system is capable of listening for
+ * the given keyphrase or not.
+ * The return code is one of {@link #KEYPHRASE_UNAVAILABLE}, {@link #KEYPHRASE_UNENROLLED}
+ * {@link #KEYPHRASE_ENROLLED} or {@link #KEYPHRASE_ACTIVE}.
+ *
+ * @param keyphrase The keyphrase whose availability is being checked.
+ * @param locale The locale for which the availability is being checked.
+ * @return Indicates if always-on hotword detection is available for the given keyphrase.
+ * TODO(sansid): Unhide this.
+ * @hide
+ */
+ public final int getAlwaysOnKeyphraseAvailability(String keyphrase, String locale) {
+ // The available keyphrases is a combination of DSP availability and
+ // the keyphrases that have an enrollment application for them.
+ if (!mSoundTriggerManager.isKeyphraseSupported(keyphrase, locale)
+ || !mKeyphraseEnrollmentInfo.isKeyphraseEnrollmentSupported(keyphrase, locale)) {
+ return KEYPHRASE_UNAVAILABLE;
+ }
+ if (!mSoundTriggerManager.isKeyphraseEnrolled(keyphrase, locale)) {
+ return KEYPHRASE_UNENROLLED;
+ }
+ if (!mSoundTriggerManager.isKeyphraseActive(keyphrase, locale)) {
+ return KEYPHRASE_ENROLLED;
+ } else {
+ return KEYPHRASE_ACTIVE;
+ }
+ }
+
+ /**
+ * @return Details of keyphrases available for enrollment.
+ * @hide
+ */
+ @VisibleForTesting
+ protected final KeyphraseEnrollmentInfo getKeyphraseEnrollmentInfo() {
+ return mKeyphraseEnrollmentInfo;
+ }
}
diff --git a/core/java/android/speech/tts/RequestConfig.java b/core/java/android/speech/tts/RequestConfig.java
index 4b5385f..84880c0 100644
--- a/core/java/android/speech/tts/RequestConfig.java
+++ b/core/java/android/speech/tts/RequestConfig.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
package android.speech.tts;
import android.media.AudioManager;
diff --git a/core/java/android/speech/tts/RequestConfigHelper.java b/core/java/android/speech/tts/RequestConfigHelper.java
index 521221b..bc65280 100644
--- a/core/java/android/speech/tts/RequestConfigHelper.java
+++ b/core/java/android/speech/tts/RequestConfigHelper.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
package android.speech.tts;
import android.speech.tts.TextToSpeechClient.EngineStatus;
diff --git a/core/java/android/speech/tts/SynthesisRequestV2.java b/core/java/android/speech/tts/SynthesisRequestV2.java
index 4ecdbc0..938458c9 100644
--- a/core/java/android/speech/tts/SynthesisRequestV2.java
+++ b/core/java/android/speech/tts/SynthesisRequestV2.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
package android.speech.tts;
import android.os.Bundle;
diff --git a/core/java/android/speech/tts/TextToSpeechClient.java b/core/java/android/speech/tts/TextToSpeechClient.java
index 5a21229..f726743 100644
--- a/core/java/android/speech/tts/TextToSpeechClient.java
+++ b/core/java/android/speech/tts/TextToSpeechClient.java
@@ -812,15 +812,14 @@
return mService != null && mEstablished;
}
- boolean runAction(Action action) {
+ <T> ActionResult<T> runAction(Action<T> action) {
synchronized (mLock) {
try {
- action.run(mService);
- return true;
+ return new ActionResult<T>(true, action.run(mService));
} catch (Exception ex) {
Log.e(TAG, action.getName() + " failed", ex);
disconnect();
- return false;
+ return new ActionResult<T>(false);
}
}
}
@@ -840,7 +839,7 @@
}
}
- private abstract class Action {
+ private abstract class Action<T> {
private final String mName;
public Action(String name) {
@@ -848,7 +847,21 @@
}
public String getName() {return mName;}
- abstract void run(ITextToSpeechService service) throws RemoteException;
+ abstract T run(ITextToSpeechService service) throws RemoteException;
+ }
+
+ private class ActionResult<T> {
+ boolean mSuccess;
+ T mResult;
+
+ ActionResult(boolean success) {
+ mSuccess = success;
+ }
+
+ ActionResult(boolean success, T result) {
+ mSuccess = success;
+ mResult = result;
+ }
}
private IBinder getCallerIdentity() {
@@ -858,18 +871,17 @@
return null;
}
- private boolean runAction(Action action) {
+ private <T> ActionResult<T> runAction(Action<T> action) {
synchronized (mLock) {
if (mServiceConnection == null) {
Log.w(TAG, action.getName() + " failed: not bound to TTS engine");
- return false;
+ return new ActionResult<T>(false);
}
if (!mServiceConnection.isEstablished()) {
Log.w(TAG, action.getName() + " failed: not fully bound to TTS engine");
- return false;
+ return new ActionResult<T>(false);
}
- mServiceConnection.runAction(action);
- return true;
+ return mServiceConnection.runAction(action);
}
}
@@ -880,13 +892,14 @@
* other utterances in the queue.
*/
public void stop() {
- runAction(new Action(ACTION_STOP_NAME) {
+ runAction(new Action<Void>(ACTION_STOP_NAME) {
@Override
- public void run(ITextToSpeechService service) throws RemoteException {
+ public Void run(ITextToSpeechService service) throws RemoteException {
if (service.stop(getCallerIdentity()) != Status.SUCCESS) {
Log.e(TAG, "Stop failed");
}
mCallbacks.clear();
+ return null;
}
});
}
@@ -934,9 +947,9 @@
final UtteranceId utteranceId,
final RequestConfig config,
final RequestCallbacks callbacks) {
- runAction(new Action(ACTION_QUEUE_SPEAK_NAME) {
+ runAction(new Action<Void>(ACTION_QUEUE_SPEAK_NAME) {
@Override
- public void run(ITextToSpeechService service) throws RemoteException {
+ public Void run(ITextToSpeechService service) throws RemoteException {
RequestCallbacks c = mDefaultRequestCallbacks;
if (callbacks != null) {
c = callbacks;
@@ -944,7 +957,7 @@
int addCallbackStatus = addCallback(utteranceId, c);
if (addCallbackStatus != Status.SUCCESS) {
c.onSynthesisFailure(utteranceId, Status.ERROR_INVALID_REQUEST);
- return;
+ return null;
}
int queueResult = service.speakV2(
@@ -953,6 +966,7 @@
if (queueResult != Status.SUCCESS) {
removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
}
+ return null;
}
});
}
@@ -1003,9 +1017,9 @@
final UtteranceId utteranceId,
final File outputFile, final RequestConfig config,
final RequestCallbacks callbacks) {
- runAction(new Action(ACTION_QUEUE_SYNTHESIZE_TO_FILE) {
+ runAction(new Action<Void>(ACTION_QUEUE_SYNTHESIZE_TO_FILE) {
@Override
- public void run(ITextToSpeechService service) throws RemoteException {
+ public Void run(ITextToSpeechService service) throws RemoteException {
RequestCallbacks c = mDefaultRequestCallbacks;
if (callbacks != null) {
c = callbacks;
@@ -1013,7 +1027,7 @@
int addCallbackStatus = addCallback(utteranceId, c);
if (addCallbackStatus != Status.SUCCESS) {
c.onSynthesisFailure(utteranceId, Status.ERROR_INVALID_REQUEST);
- return;
+ return null;
}
ParcelFileDescriptor fileDescriptor = null;
@@ -1021,7 +1035,7 @@
if (outputFile.exists() && !outputFile.canWrite()) {
Log.e(TAG, "No permissions to write to " + outputFile);
removeCallbackAndErr(utteranceId.toUniqueString(), Status.ERROR_OUTPUT);
- return;
+ return null;
}
fileDescriptor = ParcelFileDescriptor.open(outputFile,
ParcelFileDescriptor.MODE_WRITE_ONLY |
@@ -1042,6 +1056,7 @@
Log.e(TAG, "Closing file " + outputFile + " failed", e);
removeCallbackAndErr(utteranceId.toUniqueString(), Status.ERROR_OUTPUT);
}
+ return null;
}
});
}
@@ -1069,9 +1084,9 @@
*/
public void queueSilence(final long durationInMs, final UtteranceId utteranceId,
final RequestCallbacks callbacks) {
- runAction(new Action(ACTION_QUEUE_SILENCE_NAME) {
+ runAction(new Action<Void>(ACTION_QUEUE_SILENCE_NAME) {
@Override
- public void run(ITextToSpeechService service) throws RemoteException {
+ public Void run(ITextToSpeechService service) throws RemoteException {
RequestCallbacks c = mDefaultRequestCallbacks;
if (callbacks != null) {
c = callbacks;
@@ -1087,6 +1102,7 @@
if (queueResult != Status.SUCCESS) {
removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
}
+ return null;
}
});
}
@@ -1110,9 +1126,9 @@
*/
public void queueAudio(final Uri audioUrl, final UtteranceId utteranceId,
final RequestConfig config, final RequestCallbacks callbacks) {
- runAction(new Action(ACTION_QUEUE_AUDIO_NAME) {
+ runAction(new Action<Void>(ACTION_QUEUE_AUDIO_NAME) {
@Override
- public void run(ITextToSpeechService service) throws RemoteException {
+ public Void run(ITextToSpeechService service) throws RemoteException {
RequestCallbacks c = mDefaultRequestCallbacks;
if (callbacks != null) {
c = callbacks;
@@ -1128,10 +1144,35 @@
if (queueResult != Status.SUCCESS) {
removeCallbackAndErr(utteranceId.toUniqueString(), queueResult);
}
+ return null;
}
});
}
+ private static final String ACTION_IS_SPEAKING_NAME = "isSpeaking";
+
+ /**
+ * Checks whether the TTS engine is busy speaking. Note that a speech item is
+ * considered complete once it's audio data has been sent to the audio mixer, or
+ * written to a file. There might be a finite lag between this point, and when
+ * the audio hardware completes playback.
+ *
+ * @return {@code true} if the TTS engine is speaking.
+ */
+ public boolean isSpeaking() {
+ ActionResult<Boolean> result = runAction(new Action<Boolean>(ACTION_IS_SPEAKING_NAME) {
+ @Override
+ public Boolean run(ITextToSpeechService service) throws RemoteException {
+ return service.isSpeaking();
+ }
+ });
+ if (!result.mSuccess) {
+ return false; // We can't really say, return false
+ }
+ return result.mResult;
+ }
+
+
class InternalHandler extends Handler {
final static int WHAT_ENGINE_STATUS_CHANGED = 1;
final static int WHAT_SERVICE_DISCONNECTED = 2;
diff --git a/core/java/android/speech/tts/VoiceInfo.java b/core/java/android/speech/tts/VoiceInfo.java
index 16b9a97..71629dc 100644
--- a/core/java/android/speech/tts/VoiceInfo.java
+++ b/core/java/android/speech/tts/VoiceInfo.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
package android.speech.tts;
import android.os.Bundle;
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index f440853..1d9aa05 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -440,26 +440,10 @@
}
// Documentation from interface
- public SpannableStringBuilder replace(int start, int end,
+ public SpannableStringBuilder replace(final int start, final int end,
CharSequence tb, int tbstart, int tbend) {
checkRange("replace", start, end);
- // Sanity check
- if (start > end) {
- Log.w(TAG, "Bad arguments to #replace : "
- + "start = " + start + ", end = " + end);
- final int tmp = start;
- start = end;
- end = tmp;
- }
- if (tbstart > tbend) {
- Log.w(TAG, "Bad arguments to #replace : "
- + "tbstart = " + tbstart + ", tbend = " + tbend);
- final int tmp = tbstart;
- tbstart = tbend;
- tbend = tmp;
- }
-
int filtercount = mFilters.length;
for (int i = 0; i < filtercount; i++) {
CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, this, start, end);
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index f06ae71..48122d6 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -48,10 +48,11 @@
import android.text.style.UnderlineSpan;
import android.util.Log;
import android.util.Printer;
-
import android.view.View;
+
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
+
import libcore.icu.ICU;
import java.lang.reflect.Array;
@@ -229,7 +230,12 @@
public static boolean regionMatches(CharSequence one, int toffset,
CharSequence two, int ooffset,
int len) {
- char[] temp = obtain(2 * len);
+ int tempLen = 2 * len;
+ if (tempLen < len) {
+ // Integer overflow; len is unreasonably large
+ throw new IndexOutOfBoundsException();
+ }
+ char[] temp = obtain(tempLen);
getChars(one, toffset, toffset + len, temp, 0);
getChars(two, ooffset, ooffset + len, temp, len);
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index deb138d..c1341e1 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -465,32 +465,39 @@
String address;
int base = 0;
- while ((address = WebView.findAddress(string)) != null) {
- int start = string.indexOf(address);
+ try {
+ while ((address = WebView.findAddress(string)) != null) {
+ int start = string.indexOf(address);
- if (start < 0) {
- break;
+ if (start < 0) {
+ break;
+ }
+
+ LinkSpec spec = new LinkSpec();
+ int length = address.length();
+ int end = start + length;
+
+ spec.start = base + start;
+ spec.end = base + end;
+ string = string.substring(end);
+ base += end;
+
+ String encodedAddress = null;
+
+ try {
+ encodedAddress = URLEncoder.encode(address,"UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ continue;
+ }
+
+ spec.url = "geo:0,0?q=" + encodedAddress;
+ links.add(spec);
}
-
- LinkSpec spec = new LinkSpec();
- int length = address.length();
- int end = start + length;
-
- spec.start = base + start;
- spec.end = base + end;
- string = string.substring(end);
- base += end;
-
- String encodedAddress = null;
-
- try {
- encodedAddress = URLEncoder.encode(address,"UTF-8");
- } catch (UnsupportedEncodingException e) {
- continue;
- }
-
- spec.url = "geo:0,0?q=" + encodedAddress;
- links.add(spec);
+ } catch (UnsupportedOperationException e) {
+ // findAddress may fail with an unsupported exception on platforms without a WebView.
+ // In this case, we will not append anything to the links variable: it would have died
+ // in WebView.findAddress.
+ return;
}
}
diff --git a/core/java/android/transition/SidePropagation.java b/core/java/android/transition/SidePropagation.java
index 5d38ac8..623cdd1 100644
--- a/core/java/android/transition/SidePropagation.java
+++ b/core/java/android/transition/SidePropagation.java
@@ -18,6 +18,7 @@
import android.graphics.Rect;
import android.util.FloatMath;
import android.util.Log;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -32,38 +33,19 @@
public class SidePropagation extends VisibilityPropagation {
private static final String TAG = "SlidePropagation";
- /**
- * Transition propagates relative to the distance of the left side of the scene.
- */
- public static final int LEFT = Slide.LEFT;
-
- /**
- * Transition propagates relative to the distance of the top of the scene.
- */
- public static final int TOP = Slide.TOP;
-
- /**
- * Transition propagates relative to the distance of the right side of the scene.
- */
- public static final int RIGHT = Slide.RIGHT;
-
- /**
- * Transition propagates relative to the distance of the bottom of the scene.
- */
- public static final int BOTTOM = Slide.BOTTOM;
-
private float mPropagationSpeed = 3.0f;
- private int mSide = BOTTOM;
+ private int mSide = Gravity.BOTTOM;
/**
* Sets the side that is used to calculate the transition propagation. If the transitioning
* View is visible in the start of the transition, then it will transition sooner when
* closer to the side and later when farther. If the view is not visible in the start of
* the transition, then it will transition later when closer to the side and sooner when
- * farther from the edge. The default is {@link #BOTTOM}.
+ * farther from the edge. The default is {@link Gravity#BOTTOM}.
*
* @param side The side that is used to calculate the transition propagation. Must be one of
- * {@link #LEFT}, {@link #TOP}, {@link #RIGHT}, or {@link #BOTTOM}.
+ * {@link Gravity#LEFT}, {@link Gravity#TOP}, {@link Gravity#RIGHT}, or
+ * {@link Gravity#BOTTOM}.
*/
public void setSide(int side) {
mSide = side;
@@ -141,16 +123,16 @@
int left, int top, int right, int bottom) {
int distance = 0;
switch (mSide) {
- case LEFT:
+ case Gravity.LEFT:
distance = right - viewX + Math.abs(epicenterY - viewY);
break;
- case TOP:
+ case Gravity.TOP:
distance = bottom - viewY + Math.abs(epicenterX - viewX);
break;
- case RIGHT:
+ case Gravity.RIGHT:
distance = viewX - left + Math.abs(epicenterY - viewY);
break;
- case BOTTOM:
+ case Gravity.BOTTOM:
distance = viewY - top + Math.abs(epicenterX - viewX);
break;
}
@@ -159,8 +141,8 @@
private int getMaxDistance(ViewGroup sceneRoot) {
switch (mSide) {
- case LEFT:
- case RIGHT:
+ case Gravity.LEFT:
+ case Gravity.RIGHT:
return sceneRoot.getWidth();
default:
return sceneRoot.getHeight();
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 0ff8ddd..8269258 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -24,6 +24,7 @@
import android.graphics.Rect;
import android.util.Log;
import android.util.Property;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
@@ -41,35 +42,9 @@
public class Slide extends Visibility {
private static final String TAG = "Slide";
- /**
- * Move Views in or out of the left edge of the scene.
- * @see #setSlideEdge(int)
- */
- public static final int LEFT = 0;
-
- /**
- * Move Views in or out of the top edge of the scene.
- * @see #setSlideEdge(int)
- */
- public static final int TOP = 1;
-
- /**
- * Move Views in or out of the right edge of the scene.
- * @see #setSlideEdge(int)
- */
- public static final int RIGHT = 2;
-
- /**
- * Move Views in or out of the bottom edge of the scene. This is the
- * default slide direction.
- * @see #setSlideEdge(int)
- */
- public static final int BOTTOM = 3;
-
private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
- private int[] mTempLoc = new int[2];
private CalculateSlide mSlideCalculator = sCalculateBottom;
private interface CalculateSlide {
@@ -136,11 +111,11 @@
};
/**
- * Constructor using the default {@link android.transition.Slide#BOTTOM}
+ * Constructor using the default {@link Gravity#BOTTOM}
* slide edge direction.
*/
public Slide() {
- setSlideEdge(BOTTOM);
+ setSlideEdge(Gravity.BOTTOM);
}
/**
@@ -152,20 +127,22 @@
/**
* Change the edge that Views appear and disappear from.
- * @param slideEdge The edge of the scene to use for Views appearing and disappearing.
+ * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
+ * {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
+ * {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
*/
public void setSlideEdge(int slideEdge) {
switch (slideEdge) {
- case LEFT:
+ case Gravity.LEFT:
mSlideCalculator = sCalculateLeft;
break;
- case TOP:
+ case Gravity.TOP:
mSlideCalculator = sCalculateTop;
break;
- case RIGHT:
+ case Gravity.RIGHT:
mSlideCalculator = sCalculateRight;
break;
- case BOTTOM:
+ case Gravity.BOTTOM:
mSlideCalculator = sCalculateBottom;
break;
default:
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 9a70099..0a4f641 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -603,76 +603,76 @@
for (int i = 0; i < startValuesList.size(); ++i) {
TransitionValues start = startValuesList.get(i);
TransitionValues end = endValuesList.get(i);
- // Only bother trying to animate with values that differ between start/end
- if (start != null || end != null) {
- if (start == null || !start.equals(end)) {
- if (DBG) {
- View view = (end != null) ? end.view : start.view;
- Log.d(LOG_TAG, " differing start/end values for view " +
- view);
- if (start == null || end == null) {
- Log.d(LOG_TAG, " " + ((start == null) ?
- "start null, end non-null" : "start non-null, end null"));
- } else {
- for (String key : start.values.keySet()) {
- Object startValue = start.values.get(key);
- Object endValue = end.values.get(key);
- if (startValue != endValue && !startValue.equals(endValue)) {
- Log.d(LOG_TAG, " " + key + ": start(" + startValue +
- "), end(" + endValue +")");
- }
+ // Only bother trying to animate with valid values that differ between start/end
+ boolean isInvalidStart = start != null && !isValidTarget(start.view);
+ boolean isInvalidEnd = end != null && !isValidTarget(end.view);
+ boolean isChanged = start != end && (start == null || !start.equals(end));
+ if (isChanged && !isInvalidStart && !isInvalidEnd) {
+ if (DBG) {
+ View view = (end != null) ? end.view : start.view;
+ Log.d(LOG_TAG, " differing start/end values for view " + view);
+ if (start == null || end == null) {
+ Log.d(LOG_TAG, " " + ((start == null) ?
+ "start null, end non-null" : "start non-null, end null"));
+ } else {
+ for (String key : start.values.keySet()) {
+ Object startValue = start.values.get(key);
+ Object endValue = end.values.get(key);
+ if (startValue != endValue && !startValue.equals(endValue)) {
+ Log.d(LOG_TAG, " " + key + ": start(" + startValue +
+ "), end(" + endValue + ")");
}
}
}
- // TODO: what to do about targetIds and itemIds?
- Animator animator = createAnimator(sceneRoot, start, end);
+ }
+ // TODO: what to do about targetIds and itemIds?
+ Animator animator = createAnimator(sceneRoot, start, end);
+ if (animator != null) {
+ // Save animation info for future cancellation purposes
+ View view = null;
+ TransitionValues infoValues = null;
+ if (end != null) {
+ view = end.view;
+ String[] properties = getTransitionProperties();
+ if (view != null && properties != null && properties.length > 0) {
+ infoValues = new TransitionValues();
+ infoValues.view = view;
+ TransitionValues newValues = endValues.viewValues.get(view);
+ if (newValues != null) {
+ for (int j = 0; j < properties.length; ++j) {
+ infoValues.values.put(properties[j],
+ newValues.values.get(properties[j]));
+ }
+ }
+ int numExistingAnims = runningAnimators.size();
+ for (int j = 0; j < numExistingAnims; ++j) {
+ Animator anim = runningAnimators.keyAt(j);
+ AnimationInfo info = runningAnimators.get(anim);
+ if (info.values != null && info.view == view &&
+ ((info.name == null && getName() == null) ||
+ info.name.equals(getName()))) {
+ if (info.values.equals(infoValues)) {
+ // Favor the old animator
+ animator = null;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ view = (start != null) ? start.view : null;
+ }
if (animator != null) {
- // Save animation info for future cancellation purposes
- View view = null;
- TransitionValues infoValues = null;
- if (end != null) {
- view = end.view;
- String[] properties = getTransitionProperties();
- if (view != null && properties != null && properties.length > 0) {
- infoValues = new TransitionValues();
- infoValues.view = view;
- TransitionValues newValues = endValues.viewValues.get(view);
- if (newValues != null) {
- for (int j = 0; j < properties.length; ++j) {
- infoValues.values.put(properties[j],
- newValues.values.get(properties[j]));
- }
- }
- int numExistingAnims = runningAnimators.size();
- for (int j = 0; j < numExistingAnims; ++j) {
- Animator anim = runningAnimators.keyAt(j);
- AnimationInfo info = runningAnimators.get(anim);
- if (info.values != null && info.view == view &&
- ((info.name == null && getName() == null) ||
- info.name.equals(getName()))) {
- if (info.values.equals(infoValues)) {
- // Favor the old animator
- animator = null;
- break;
- }
- }
- }
- }
- } else {
- view = (start != null) ? start.view : null;
+ if (mPropagation != null) {
+ long delay = mPropagation
+ .getStartDelay(sceneRoot, this, start, end);
+ startDelays.put(mAnimators.size(), delay);
+ minStartDelay = Math.min(delay, minStartDelay);
}
- if (animator != null) {
- if (mPropagation != null) {
- long delay = mPropagation
- .getStartDelay(sceneRoot, this, start, end);
- startDelays.put(mAnimators.size(), delay);
- minStartDelay = Math.min(delay, minStartDelay);
- }
- AnimationInfo info = new AnimationInfo(view, getName(),
- sceneRoot.getWindowId(), infoValues);
- runningAnimators.put(animator, info);
- mAnimators.add(animator);
- }
+ AnimationInfo info = new AnimationInfo(view, getName(),
+ sceneRoot.getWindowId(), infoValues);
+ runningAnimators.put(animator, info);
+ mAnimators.add(animator);
}
}
}
@@ -1763,7 +1763,7 @@
/**
* Sets the callback to use to find the epicenter of a Transition. A null value indicates
- * that there is no epicenter in the Transition and getEpicenter() will return null.
+ * that there is no epicenter in the Transition and onGetEpicenter() will return null.
* Transitions like {@link android.transition.Explode} use a point or Rect to orient
* the direction of travel. This is called the epicenter of the Transition and is
* typically centered on a touched View. The
@@ -1799,7 +1799,7 @@
if (mEpicenterCallback == null) {
return null;
}
- return mEpicenterCallback.getEpicenter(this);
+ return mEpicenterCallback.onGetEpicenter(this);
}
/**
@@ -2112,6 +2112,6 @@
* @return The Rect region of the epicenter of <code>transition</code> or null if
* there is no epicenter.
*/
- public abstract Rect getEpicenter(Transition transition);
+ public abstract Rect onGetEpicenter(Transition transition);
}
}
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index f4b562f..5b7c737 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -22,6 +22,7 @@
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.Xml;
+import android.view.Gravity;
import android.view.InflateException;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
@@ -208,7 +209,7 @@
private Slide createSlideTransition(AttributeSet attrs) {
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.Slide);
- int edge = a.getInt(com.android.internal.R.styleable.Slide_slideEdge, Slide.BOTTOM);
+ int edge = a.getInt(com.android.internal.R.styleable.Slide_slideEdge, Gravity.BOTTOM);
Slide slide = new Slide(edge);
a.recycle();
return slide;
diff --git a/core/java/android/tv/ITvInputClient.aidl b/core/java/android/tv/ITvInputClient.aidl
deleted file mode 100644
index ef89c68..0000000
--- a/core/java/android/tv/ITvInputClient.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.tv.ITvInputSession;
-import android.view.InputChannel;
-
-/**
- * Interface a client of the ITvInputManager implements, to identify itself and receive information
- * about changes to the state of each TV input service.
- * @hide
- */
-oneway interface ITvInputClient {
- void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
- void onAvailabilityChanged(in String inputId, boolean isAvailable);
- void onSessionReleased(int seq);
- void onSessionEvent(in String name, in Bundle args, int seq);
- void onVideoSizeChanged(int width, int height, int seq);
-}
diff --git a/core/java/android/tv/ITvInputHardware.aidl b/core/java/android/tv/ITvInputHardware.aidl
deleted file mode 100644
index 7250453..0000000
--- a/core/java/android/tv/ITvInputHardware.aidl
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.tv.TvStreamConfig;
-import android.view.KeyEvent;
-import android.view.Surface;
-
-/**
- * TvInputService representing a physical port should connect to HAL through this interface.
- * Framework will take care of communication among system services including TvInputManagerService,
- * HdmiControlService, AudioService, etc.
- *
- * @hide
- */
-interface ITvInputHardware {
- /**
- * Make the input render on the surface according to the config. In case of HDMI, this will
- * trigger CEC commands for adjusting active HDMI source. Returns true on success.
- */
- boolean setSurface(in Surface surface, in TvStreamConfig config);
- /**
- * Set volume for this stream via AudioGain. (TBD)
- */
- void setVolume(float volume);
-
- /**
- * Dispatch key event to HDMI service. The events would be automatically converted to
- * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail.
- */
- boolean dispatchKeyEventToHdmi(in KeyEvent event);
-}
diff --git a/core/java/android/tv/ITvInputHardwareCallback.aidl b/core/java/android/tv/ITvInputHardwareCallback.aidl
deleted file mode 100644
index 83041be..0000000
--- a/core/java/android/tv/ITvInputHardwareCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.tv.TvStreamConfig;
-
-/**
- * @hide
- */
-oneway interface ITvInputHardwareCallback {
- void onReleased();
- void onStreamConfigChanged(in TvStreamConfig[] configs);
-}
diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl
deleted file mode 100644
index c6f8d79..0000000
--- a/core/java/android/tv/ITvInputManager.aidl
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.content.ComponentName;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.tv.ITvInputHardware;
-import android.tv.ITvInputHardwareCallback;
-import android.tv.ITvInputClient;
-import android.tv.TvInputHardwareInfo;
-import android.tv.TvInputInfo;
-import android.view.Surface;
-
-/**
- * Interface to the TV input manager service.
- * @hide
- */
-interface ITvInputManager {
- List<TvInputInfo> getTvInputList(int userId);
-
- boolean getAvailability(in ITvInputClient client, in String inputId, int userId);
-
- void registerCallback(in ITvInputClient client, in String inputId, int userId);
- void unregisterCallback(in ITvInputClient client, in String inputId, int userId);
-
- void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
- void releaseSession(in IBinder sessionToken, int userId);
-
- void setSurface(in IBinder sessionToken, in Surface surface, int userId);
- void setVolume(in IBinder sessionToken, float volume, int userId);
- void tune(in IBinder sessionToken, in Uri channelUri, int userId);
-
- void createOverlayView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
- int userId);
- void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
- void removeOverlayView(in IBinder sessionToken, int userId);
-
- // For TV input hardware binding
- List<TvInputHardwareInfo> getHardwareList();
- ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
- int userId);
- void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
-}
diff --git a/core/java/android/tv/ITvInputService.aidl b/core/java/android/tv/ITvInputService.aidl
deleted file mode 100644
index 4f1bc2b..0000000
--- a/core/java/android/tv/ITvInputService.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.tv.ITvInputServiceCallback;
-import android.tv.ITvInputSessionCallback;
-import android.view.InputChannel;
-
-/**
- * Top-level interface to a TV input component (implemented in a Service).
- * @hide
- */
-oneway interface ITvInputService {
- void registerCallback(ITvInputServiceCallback callback);
- void unregisterCallback(in ITvInputServiceCallback callback);
- void createSession(in InputChannel channel, ITvInputSessionCallback callback);
-}
diff --git a/core/java/android/tv/ITvInputServiceCallback.aidl b/core/java/android/tv/ITvInputServiceCallback.aidl
deleted file mode 100644
index 71fc780..0000000
--- a/core/java/android/tv/ITvInputServiceCallback.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.content.ComponentName;
-
-/**
- * Helper interface for ITvInputService to allow the TV input to notify the client when its status
- * has been changed.
- * @hide
- */
-oneway interface ITvInputServiceCallback {
- void onAvailabilityChanged(in String inputId, boolean isAvailable);
-}
diff --git a/core/java/android/tv/ITvInputSession.aidl b/core/java/android/tv/ITvInputSession.aidl
deleted file mode 100644
index 32fee4b..0000000
--- a/core/java/android/tv/ITvInputSession.aidl
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.graphics.Rect;
-import android.net.Uri;
-import android.view.Surface;
-
-/**
- * Sub-interface of ITvInputService which is created per session and has its own context.
- * @hide
- */
-oneway interface ITvInputSession {
- void release();
-
- void setSurface(in Surface surface);
- // TODO: Remove this once it becomes irrelevant for applications to handle audio focus. The plan
- // is to introduce some new concepts that will solve a number of problems in audio policy today.
- void setVolume(float volume);
- void tune(in Uri channelUri);
-
- void createOverlayView(in IBinder windowToken, in Rect frame);
- void relayoutOverlayView(in Rect frame);
- void removeOverlayView();
-}
diff --git a/core/java/android/tv/ITvInputSessionCallback.aidl b/core/java/android/tv/ITvInputSessionCallback.aidl
deleted file mode 100644
index e27b8bf..0000000
--- a/core/java/android/tv/ITvInputSessionCallback.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.os.Bundle;
-import android.tv.ITvInputSession;
-
-/**
- * Helper interface for ITvInputSession to allow the TV input to notify the system service when a
- * new session has been created.
- * @hide
- */
-oneway interface ITvInputSessionCallback {
- void onSessionCreated(ITvInputSession session);
- void onSessionEvent(in String name, in Bundle args);
- void onVideoSizeChanged(int width, int height);
-}
diff --git a/core/java/android/tv/ITvInputSessionWrapper.java b/core/java/android/tv/ITvInputSessionWrapper.java
deleted file mode 100644
index 3ccccf3..0000000
--- a/core/java/android/tv/ITvInputSessionWrapper.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.tv.TvInputManager.Session;
-import android.tv.TvInputService.TvInputSessionImpl;
-import android.util.Log;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.Surface;
-
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
-
-/**
- * Implements the internal ITvInputSession interface to convert incoming calls on to it back to
- * calls on the public TvInputSession interface, scheduling them on the main thread of the process.
- *
- * @hide
- */
-public class ITvInputSessionWrapper extends ITvInputSession.Stub implements HandlerCaller.Callback {
- private static final String TAG = "TvInputSessionWrapper";
-
- private static final int DO_RELEASE = 1;
- private static final int DO_SET_SURFACE = 2;
- private static final int DO_SET_VOLUME = 3;
- private static final int DO_TUNE = 4;
- private static final int DO_CREATE_OVERLAY_VIEW = 5;
- private static final int DO_RELAYOUT_OVERLAY_VIEW = 6;
- private static final int DO_REMOVE_OVERLAY_VIEW = 7;
-
- private final HandlerCaller mCaller;
-
- private TvInputSessionImpl mTvInputSessionImpl;
- private InputChannel mChannel;
- private TvInputEventReceiver mReceiver;
-
- public ITvInputSessionWrapper(Context context, TvInputSessionImpl sessionImpl,
- InputChannel channel) {
- mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
- mTvInputSessionImpl = sessionImpl;
- mChannel = channel;
- if (channel != null) {
- mReceiver = new TvInputEventReceiver(channel, context.getMainLooper());
- }
- }
-
- @Override
- public void executeMessage(Message msg) {
- if (mTvInputSessionImpl == null) {
- return;
- }
-
- switch (msg.what) {
- case DO_RELEASE: {
- mTvInputSessionImpl.release();
- mTvInputSessionImpl = null;
- if (mReceiver != null) {
- mReceiver.dispose();
- mReceiver = null;
- }
- if (mChannel != null) {
- mChannel.dispose();
- mChannel = null;
- }
- return;
- }
- case DO_SET_SURFACE: {
- mTvInputSessionImpl.setSurface((Surface) msg.obj);
- return;
- }
- case DO_SET_VOLUME: {
- mTvInputSessionImpl.setVolume((Float) msg.obj);
- return;
- }
- case DO_TUNE: {
- mTvInputSessionImpl.tune((Uri) msg.obj);
- return;
- }
- case DO_CREATE_OVERLAY_VIEW: {
- SomeArgs args = (SomeArgs) msg.obj;
- mTvInputSessionImpl.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
- args.recycle();
- return;
- }
- case DO_RELAYOUT_OVERLAY_VIEW: {
- mTvInputSessionImpl.relayoutOverlayView((Rect) msg.obj);
- return;
- }
- case DO_REMOVE_OVERLAY_VIEW: {
- mTvInputSessionImpl.removeOverlayView(true);
- return;
- }
- default: {
- Log.w(TAG, "Unhandled message code: " + msg.what);
- return;
- }
- }
- }
-
- @Override
- public void release() {
- mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE));
- }
-
- @Override
- public void setSurface(Surface surface) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
- }
-
- @Override
- public final void setVolume(float volume) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_VOLUME, volume));
- }
-
- @Override
- public void tune(Uri channelUri) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TUNE, channelUri));
- }
-
- @Override
- public void createOverlayView(IBinder windowToken, Rect frame) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_OVERLAY_VIEW, windowToken,
- frame));
- }
-
- @Override
- public void relayoutOverlayView(Rect frame) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_OVERLAY_VIEW, frame));
- }
-
- @Override
- public void removeOverlayView() {
- mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW));
- }
-
- private final class TvInputEventReceiver extends InputEventReceiver {
- public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- @Override
- public void onInputEvent(InputEvent event) {
- if (mTvInputSessionImpl == null) {
- // The session has been finished.
- finishInputEvent(event, false);
- return;
- }
-
- int handled = mTvInputSessionImpl.dispatchInputEvent(event, this);
- if (handled != Session.DISPATCH_IN_PROGRESS) {
- finishInputEvent(event, handled == Session.DISPATCH_HANDLED);
- }
- }
- }
-}
diff --git a/core/java/android/tv/TvInputHardwareInfo.aidl b/core/java/android/tv/TvInputHardwareInfo.aidl
deleted file mode 100644
index 484ab60..0000000
--- a/core/java/android/tv/TvInputHardwareInfo.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-parcelable TvInputHardwareInfo;
diff --git a/core/java/android/tv/TvInputHardwareInfo.java b/core/java/android/tv/TvInputHardwareInfo.java
deleted file mode 100644
index b0dc58e..0000000
--- a/core/java/android/tv/TvInputHardwareInfo.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * Simple container for information about TV input hardware.
- * Not for third-party developers.
- *
- * @hide
- */
-public final class TvInputHardwareInfo implements Parcelable {
- static final String TAG = "TvInputHardwareInfo";
-
- // Match hardware/libhardware/include/hardware/tv_input.h
- public static final int TV_INPUT_TYPE_HDMI = 1;
- public static final int TV_INPUT_TYPE_BUILT_IN_TUNER = 2;
- public static final int TV_INPUT_TYPE_PASSTHROUGH = 3;
-
- public static final Parcelable.Creator<TvInputHardwareInfo> CREATOR =
- new Parcelable.Creator<TvInputHardwareInfo>() {
- @Override
- public TvInputHardwareInfo createFromParcel(Parcel source) {
- try {
- TvInputHardwareInfo info = new TvInputHardwareInfo();
- info.readFromParcel(source);
- return info;
- } catch (Exception e) {
- Log.e(TAG, "Exception creating TvInputHardwareInfo from parcel", e);
- return null;
- }
- }
-
- @Override
- public TvInputHardwareInfo[] newArray(int size) {
- return new TvInputHardwareInfo[size];
- }
- };
-
- private int mDeviceId;
- private int mType;
- // TODO: Add audio port & audio address for audio service.
- // TODO: Add HDMI handle for HDMI service.
-
- public TvInputHardwareInfo() { }
-
- public TvInputHardwareInfo(int deviceId, int type) {
- mDeviceId = deviceId;
- mType = type;
- }
-
- public int getDeviceId() {
- return mDeviceId;
- }
-
- public int getType() {
- return mType;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mDeviceId);
- dest.writeInt(mType);
- }
-
- public void readFromParcel(Parcel source) {
- mDeviceId = source.readInt();
- mType = source.readInt();
- }
-}
diff --git a/core/java/android/tv/TvInputInfo.aidl b/core/java/android/tv/TvInputInfo.aidl
deleted file mode 100644
index abc4b47..0000000
--- a/core/java/android/tv/TvInputInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-parcelable TvInputInfo;
diff --git a/core/java/android/tv/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java
deleted file mode 100644
index 217e4b7..0000000
--- a/core/java/android/tv/TvInputInfo.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * This class is used to specify meta information of a TV input.
- */
-public final class TvInputInfo implements Parcelable {
- private final ResolveInfo mService;
- private final String mId;
-
- /**
- * Constructor.
- *
- * @param service The ResolveInfo returned from the package manager about this TV input service.
- * @hide
- */
- public TvInputInfo(ResolveInfo service) {
- mService = service;
- ServiceInfo si = service.serviceInfo;
- mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
- }
-
- /**
- * Returns a unique ID for this TV input. The ID is generated from the package and class name
- * implementing the TV input service.
- */
- public String getId() {
- return mId;
- }
-
- /**
- * Returns the .apk package that implements this TV input service.
- */
- public String getPackageName() {
- return mService.serviceInfo.packageName;
- }
-
- /**
- * Returns the class name of the service component that implements this TV input service.
- */
- public String getServiceName() {
- return mService.serviceInfo.name;
- }
-
- /**
- * Returns the component of the service that implements this TV input.
- */
- public ComponentName getComponent() {
- return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
- }
-
- /**
- * Loads the user-displayed label for this TV input service.
- *
- * @param pm Supplies a PackageManager used to load the TV input's resources.
- * @return a CharSequence containing the TV input's label. If the TV input does not have
- * a label, its name is returned.
- */
- public CharSequence loadLabel(PackageManager pm) {
- return mService.loadLabel(pm);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public int hashCode() {
- return mId.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
-
- if (!(o instanceof TvInputInfo)) {
- return false;
- }
-
- TvInputInfo obj = (TvInputInfo) o;
- return mId.equals(obj.mId)
- && mService.serviceInfo.packageName.equals(obj.mService.serviceInfo.packageName)
- && mService.serviceInfo.name.equals(obj.mService.serviceInfo.name);
- }
-
- @Override
- public String toString() {
- return "TvInputInfo{id=" + mId
- + ", pkg=" + mService.serviceInfo.packageName
- + ", service=" + mService.serviceInfo.name + "}";
- }
-
- /**
- * Used to package this object into a {@link Parcel}.
- *
- * @param dest The {@link Parcel} to be written.
- * @param flags The flags used for parceling.
- */
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mId);
- mService.writeToParcel(dest, flags);
- }
-
- /**
- * Used to generate an input id from a ComponentName.
- *
- * @param name the component name for generating an input id.
- * @return the generated input id for the given {@code name}.
- * @hide
- */
- public static final String generateInputIdForComponentName(ComponentName name) {
- return name.flattenToShortString();
- }
-
- /**
- * Used to make this class parcelable.
- *
- * @hide
- */
- public static final Parcelable.Creator<TvInputInfo> CREATOR =
- new Parcelable.Creator<TvInputInfo>() {
- @Override
- public TvInputInfo createFromParcel(Parcel in) {
- return new TvInputInfo(in);
- }
-
- @Override
- public TvInputInfo[] newArray(int size) {
- return new TvInputInfo[size];
- }
- };
-
- private TvInputInfo(Parcel in) {
- mId = in.readString();
- mService = ResolveInfo.CREATOR.createFromParcel(in);
- }
-}
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
deleted file mode 100644
index d0c2ca6..0000000
--- a/core/java/android/tv/TvInputManager.java
+++ /dev/null
@@ -1,850 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Pools.Pool;
-import android.util.Pools.SimplePool;
-import android.util.SparseArray;
-import android.view.InputChannel;
-import android.view.InputEvent;
-import android.view.InputEventSender;
-import android.view.Surface;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Central system API to the overall TV input framework (TIF) architecture, which arbitrates
- * interaction between applications and the selected TV inputs.
- */
-public final class TvInputManager {
- private static final String TAG = "TvInputManager";
-
- private final ITvInputManager mService;
-
- // A mapping from an input to the list of its TvInputListenerRecords.
- private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
- new HashMap<String, List<TvInputListenerRecord>>();
-
- // A mapping from the sequence number of a session to its SessionCallbackRecord.
- private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
- new SparseArray<SessionCallbackRecord>();
-
- // A sequence number for the next session to be created. Should be protected by a lock
- // {@code mSessionCallbackRecordMap}.
- private int mNextSeq;
-
- private final ITvInputClient mClient;
-
- private final int mUserId;
-
- /**
- * Interface used to receive the created session.
- */
- public abstract static class SessionCallback {
- /**
- * This is called after {@link TvInputManager#createSession} has been processed.
- *
- * @param session A {@link TvInputManager.Session} instance created. This can be
- * {@code null} if the creation request failed.
- */
- public void onSessionCreated(Session session) {
- }
-
- /**
- * This is called when {@link TvInputManager.Session} is released.
- * This typically happens when the process hosting the session has crashed or been killed.
- *
- * @param session A {@link TvInputManager.Session} instance released.
- */
- public void onSessionReleased(Session session) {
- }
-
- /**
- * This is called at the beginning of the playback of a channel and later when the size of
- * the video has been changed.
- *
- * @param session A {@link TvInputManager.Session} associated with this callback
- * @param width the width of the video
- * @param height the height of the video
- * @hide
- */
- public void onVideoSizeChanged(Session session, int width, int height) {
- }
-
- /**
- * This is called when a custom event has been sent from this session.
- *
- * @param session A {@link TvInputManager.Session} associated with this callback
- * @param eventType The type of the event.
- * @param eventArgs Optional arguments of the event.
- * @hide
- */
- public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
- }
- }
-
- private static final class SessionCallbackRecord {
- private final SessionCallback mSessionCallback;
- private final Handler mHandler;
- private Session mSession;
-
- public SessionCallbackRecord(SessionCallback sessionCallback,
- Handler handler) {
- mSessionCallback = sessionCallback;
- mHandler = handler;
- }
-
- public void postSessionCreated(final Session session) {
- mSession = session;
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mSessionCallback.onSessionCreated(session);
- }
- });
- }
-
- public void postSessionReleased() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mSessionCallback.onSessionReleased(mSession);
- }
- });
- }
-
- public void postVideoSizeChanged(final int width, final int height) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mSessionCallback.onVideoSizeChanged(mSession, width, height);
- }
- });
- }
-
- public void postSessionEvent(final String eventType, final Bundle eventArgs) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mSessionCallback.onSessionEvent(mSession, eventType, eventArgs);
- }
- });
- }
- }
-
- /**
- * Interface used to monitor status of the TV input.
- */
- public abstract static class TvInputListener {
- /**
- * This is called when the availability status of a given TV input is changed.
- *
- * @param inputId the id of the TV input.
- * @param isAvailable {@code true} if the given TV input is available to show TV programs.
- * {@code false} otherwise.
- */
- public void onAvailabilityChanged(String inputId, boolean isAvailable) {
- }
- }
-
- private static final class TvInputListenerRecord {
- private final TvInputListener mListener;
- private final Handler mHandler;
-
- public TvInputListenerRecord(TvInputListener listener, Handler handler) {
- mListener = listener;
- mHandler = handler;
- }
-
- public TvInputListener getListener() {
- return mListener;
- }
-
- public void postAvailabilityChanged(final String inputId, final boolean isAvailable) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onAvailabilityChanged(inputId, isAvailable);
- }
- });
- }
- }
-
- /**
- * @hide
- */
- public TvInputManager(ITvInputManager service, int userId) {
- mService = service;
- mUserId = userId;
- mClient = new ITvInputClient.Stub() {
- @Override
- public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
- int seq) {
- synchronized (mSessionCallbackRecordMap) {
- SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
- if (record == null) {
- Log.e(TAG, "Callback not found for " + token);
- return;
- }
- Session session = null;
- if (token != null) {
- session = new Session(token, channel, mService, mUserId, seq,
- mSessionCallbackRecordMap);
- }
- record.postSessionCreated(session);
- }
- }
-
- @Override
- public void onSessionReleased(int seq) {
- synchronized (mSessionCallbackRecordMap) {
- SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
- mSessionCallbackRecordMap.delete(seq);
- if (record == null) {
- Log.e(TAG, "Callback not found for seq:" + seq);
- return;
- }
- record.mSession.releaseInternal();
- record.postSessionReleased();
- }
- }
-
- @Override
- public void onVideoSizeChanged(int width, int height, int seq) {
- synchronized (mSessionCallbackRecordMap) {
- SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
- if (record == null) {
- Log.e(TAG, "Callback not found for seq " + seq);
- return;
- }
- record.postVideoSizeChanged(width, height);
- }
- }
-
- @Override
- public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
- synchronized (mSessionCallbackRecordMap) {
- SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
- if (record == null) {
- Log.e(TAG, "Callback not found for seq " + seq);
- return;
- }
- record.postSessionEvent(eventType, eventArgs);
- }
- }
-
- @Override
- public void onAvailabilityChanged(String inputId, boolean isAvailable) {
- synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
- if (records == null) {
- // Silently ignore - no listener is registered yet.
- return;
- }
- int recordsCount = records.size();
- for (int i = 0; i < recordsCount; i++) {
- records.get(i).postAvailabilityChanged(inputId, isAvailable);
- }
- }
- }
- };
- }
-
- /**
- * Returns the complete list of TV inputs on the system.
- *
- * @return List of {@link TvInputInfo} for each TV input that describes its meta information.
- */
- public List<TvInputInfo> getTvInputList() {
- try {
- return mService.getTvInputList(mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Returns the availability of a given TV input.
- *
- * @param inputId the id of the TV input.
- * @throws IllegalArgumentException if the argument is {@code null}.
- * @throws IllegalStateException If there is no {@link TvInputListener} registered on the given
- * TV input.
- */
- public boolean getAvailability(String inputId) {
- if (inputId == null) {
- throw new IllegalArgumentException("id cannot be null");
- }
- synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
- if (records == null || records.size() == 0) {
- throw new IllegalStateException("At least one listener should be registered.");
- }
- }
- try {
- return mService.getAvailability(mClient, inputId, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Registers a {@link TvInputListener} for a given TV input.
- *
- * @param inputId the id of the TV input.
- * @param listener a listener used to monitor status of the given TV input.
- * @param handler a {@link Handler} that the status change will be delivered to.
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
- */
- public void registerListener(String inputId, TvInputListener listener, Handler handler) {
- if (inputId == null) {
- throw new IllegalArgumentException("id cannot be null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("listener cannot be null");
- }
- if (handler == null) {
- throw new IllegalArgumentException("handler cannot be null");
- }
- synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
- if (records == null) {
- records = new ArrayList<TvInputListenerRecord>();
- mTvInputListenerRecordsMap.put(inputId, records);
- try {
- mService.registerCallback(mClient, inputId, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
- records.add(new TvInputListenerRecord(listener, handler));
- }
- }
-
- /**
- * Unregisters the existing {@link TvInputListener} for a given TV input.
- *
- * @param inputId the id of the TV input.
- * @param listener the existing listener to remove for the given TV input.
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
- */
- public void unregisterListener(String inputId, final TvInputListener listener) {
- if (inputId == null) {
- throw new IllegalArgumentException("id cannot be null");
- }
- if (listener == null) {
- throw new IllegalArgumentException("listener cannot be null");
- }
- synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
- if (records == null) {
- Log.e(TAG, "No listener found for " + inputId);
- return;
- }
- for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) {
- TvInputListenerRecord record = it.next();
- if (record.getListener() == listener) {
- it.remove();
- }
- }
- if (records.isEmpty()) {
- try {
- mService.unregisterCallback(mClient, inputId, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- } finally {
- mTvInputListenerRecordsMap.remove(inputId);
- }
- }
- }
- }
-
- /**
- * Creates a {@link Session} for a given TV input.
- * <p>
- * The number of sessions that can be created at the same time is limited by the capability of
- * the given TV input.
- * </p>
- *
- * @param inputId the id of the TV input.
- * @param callback a callback used to receive the created session.
- * @param handler a {@link Handler} that the session creation will be delivered to.
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
- */
- public void createSession(String inputId, final SessionCallback callback,
- Handler handler) {
- if (inputId == null) {
- throw new IllegalArgumentException("id cannot be null");
- }
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- if (handler == null) {
- throw new IllegalArgumentException("handler cannot be null");
- }
- SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
- synchronized (mSessionCallbackRecordMap) {
- int seq = mNextSeq++;
- mSessionCallbackRecordMap.put(seq, record);
- try {
- mService.createSession(mClient, inputId, seq, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- /** The Session provides the per-session functionality of TV inputs. */
- public static final class Session {
- static final int DISPATCH_IN_PROGRESS = -1;
- static final int DISPATCH_NOT_HANDLED = 0;
- static final int DISPATCH_HANDLED = 1;
-
- private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
-
- private final ITvInputManager mService;
- private final int mUserId;
- private final int mSeq;
-
- // For scheduling input event handling on the main thread. This also serves as a lock to
- // protect pending input events and the input channel.
- private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
-
- private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
- private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
- private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
-
- private IBinder mToken;
- private TvInputEventSender mSender;
- private InputChannel mChannel;
-
- /** @hide */
- private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
- int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
- mToken = token;
- mChannel = channel;
- mService = service;
- mUserId = userId;
- mSeq = seq;
- mSessionCallbackRecordMap = sessionCallbackRecordMap;
- }
-
- /**
- * Releases this session.
- *
- * @throws IllegalStateException if the session has been already released.
- */
- public void release() {
- if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
- }
- try {
- mService.releaseSession(mToken, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
-
- releaseInternal();
- }
-
- /**
- * Sets the {@link android.view.Surface} for this session.
- *
- * @param surface A {@link android.view.Surface} used to render video.
- * @throws IllegalStateException if the session has been already released.
- * @hide
- */
- public void setSurface(Surface surface) {
- if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
- }
- // surface can be null.
- try {
- mService.setSurface(mToken, surface, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Sets the relative volume of this session to handle a change of audio focus.
- *
- * @param volume A volume value between 0.0f to 1.0f.
- * @throws IllegalArgumentException if the volume value is out of range.
- * @throws IllegalStateException if the session has been already released.
- */
- public void setVolume(float volume) {
- if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
- }
- try {
- if (volume < 0.0f || volume > 1.0f) {
- throw new IllegalArgumentException("volume should be between 0.0f and 1.0f");
- }
- mService.setVolume(mToken, volume, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Tunes to a given channel.
- *
- * @param channelUri The URI of a channel.
- * @throws IllegalArgumentException if the argument is {@code null}.
- * @throws IllegalStateException if the session has been already released.
- */
- public void tune(Uri channelUri) {
- if (channelUri == null) {
- throw new IllegalArgumentException("channelUri cannot be null");
- }
- if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
- }
- try {
- mService.tune(mToken, channelUri, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
- * should be called whenever the layout of its containing view is changed.
- * {@link #removeOverlayView()} should be called to remove the overlay view.
- * Since a session can have only one overlay view, this method should be called only once
- * or it can be called again after calling {@link #removeOverlayView()}.
- *
- * @param view A view playing TV.
- * @param frame A position of the overlay view.
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
- * @throws IllegalStateException if {@code view} is not attached to a window or
- * if the session has been already released.
- */
- void createOverlayView(View view, Rect frame) {
- if (view == null) {
- throw new IllegalArgumentException("view cannot be null");
- }
- if (frame == null) {
- throw new IllegalArgumentException("frame cannot be null");
- }
- if (view.getWindowToken() == null) {
- throw new IllegalStateException("view must be attached to a window");
- }
- if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
- }
- try {
- mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Relayouts the current overlay view.
- *
- * @param frame A new position of the overlay view.
- * @throws IllegalArgumentException if the arguments is {@code null}.
- * @throws IllegalStateException if the session has been already released.
- */
- void relayoutOverlayView(Rect frame) {
- if (frame == null) {
- throw new IllegalArgumentException("frame cannot be null");
- }
- if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
- }
- try {
- mService.relayoutOverlayView(mToken, frame, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Removes the current overlay view.
- *
- * @throws IllegalStateException if the session has been already released.
- */
- void removeOverlayView() {
- if (mToken == null) {
- throw new IllegalStateException("the session has been already released");
- }
- try {
- mService.removeOverlayView(mToken, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Dispatches an input event to this session.
- *
- * @param event {@link InputEvent} to dispatch.
- * @param token A token used to identify the input event later in the callback.
- * @param callback A callback used to receive the dispatch result.
- * @param handler {@link Handler} that the dispatch result will be delivered to.
- * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
- * {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
- * {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
- * be invoked later.
- * @throws IllegalArgumentException if any of the necessary arguments is {@code null}.
- * @hide
- */
- public int dispatchInputEvent(InputEvent event, Object token,
- FinishedInputEventCallback callback, Handler handler) {
- if (event == null) {
- throw new IllegalArgumentException("event cannot be null");
- }
- if (callback != null && handler == null) {
- throw new IllegalArgumentException("handler cannot be null");
- }
- synchronized (mHandler) {
- if (mChannel == null) {
- return DISPATCH_NOT_HANDLED;
- }
- PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
- if (Looper.myLooper() == Looper.getMainLooper()) {
- // Already running on the main thread so we can send the event immediately.
- return sendInputEventOnMainLooperLocked(p);
- }
-
- // Post the event to the main thread.
- Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
- msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
- return DISPATCH_IN_PROGRESS;
- }
- }
-
- /**
- * Callback that is invoked when an input event that was dispatched to this session has been
- * finished.
- *
- * @hide
- */
- public interface FinishedInputEventCallback {
- /**
- * Called when the dispatched input event is finished.
- *
- * @param token a token passed to {@link #dispatchInputEvent}.
- * @param handled {@code true} if the dispatched input event was handled properly.
- * {@code false} otherwise.
- */
- public void onFinishedInputEvent(Object token, boolean handled);
- }
-
- // Must be called on the main looper
- private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
- synchronized (mHandler) {
- int result = sendInputEventOnMainLooperLocked(p);
- if (result == DISPATCH_IN_PROGRESS) {
- return;
- }
- }
-
- invokeFinishedInputEventCallback(p, false);
- }
-
- private int sendInputEventOnMainLooperLocked(PendingEvent p) {
- if (mChannel != null) {
- if (mSender == null) {
- mSender = new TvInputEventSender(mChannel, mHandler.getLooper());
- }
-
- final InputEvent event = p.mEvent;
- final int seq = event.getSequenceNumber();
- if (mSender.sendInputEvent(seq, event)) {
- mPendingEvents.put(seq, p);
- Message msg = mHandler.obtainMessage(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
- msg.setAsynchronous(true);
- mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
- return DISPATCH_IN_PROGRESS;
- }
-
- Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
- + event);
- }
- return DISPATCH_NOT_HANDLED;
- }
-
- void finishedInputEvent(int seq, boolean handled, boolean timeout) {
- final PendingEvent p;
- synchronized (mHandler) {
- int index = mPendingEvents.indexOfKey(seq);
- if (index < 0) {
- return; // spurious, event already finished or timed out
- }
-
- p = mPendingEvents.valueAt(index);
- mPendingEvents.removeAt(index);
-
- if (timeout) {
- Log.w(TAG, "Timeout waiting for seesion to handle input event after "
- + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
- } else {
- mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
- }
- }
-
- invokeFinishedInputEventCallback(p, handled);
- }
-
- // Assumes the event has already been removed from the queue.
- void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
- p.mHandled = handled;
- if (p.mHandler.getLooper().isCurrentThread()) {
- // Already running on the callback handler thread so we can send the callback
- // immediately.
- p.run();
- } else {
- // Post the event to the callback handler thread.
- // In this case, the callback will be responsible for recycling the event.
- Message msg = Message.obtain(p.mHandler, p);
- msg.setAsynchronous(true);
- msg.sendToTarget();
- }
- }
-
- private void flushPendingEventsLocked() {
- mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
-
- final int count = mPendingEvents.size();
- for (int i = 0; i < count; i++) {
- int seq = mPendingEvents.keyAt(i);
- Message msg = mHandler.obtainMessage(InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
- msg.setAsynchronous(true);
- msg.sendToTarget();
- }
- }
-
- private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
- FinishedInputEventCallback callback, Handler handler) {
- PendingEvent p = mPendingEventPool.acquire();
- if (p == null) {
- p = new PendingEvent();
- }
- p.mEvent = event;
- p.mToken = token;
- p.mCallback = callback;
- p.mHandler = handler;
- return p;
- }
-
- private void recyclePendingEventLocked(PendingEvent p) {
- p.recycle();
- mPendingEventPool.release(p);
- }
-
- private void releaseInternal() {
- mToken = null;
- synchronized (mHandler) {
- if (mChannel != null) {
- if (mSender != null) {
- flushPendingEventsLocked();
- mSender.dispose();
- mSender = null;
- }
- mChannel.dispose();
- mChannel = null;
- }
- }
- synchronized (mSessionCallbackRecordMap) {
- mSessionCallbackRecordMap.remove(mSeq);
- }
- }
-
- private final class InputEventHandler extends Handler {
- public static final int MSG_SEND_INPUT_EVENT = 1;
- public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
- public static final int MSG_FLUSH_INPUT_EVENT = 3;
-
- InputEventHandler(Looper looper) {
- super(looper, null, true);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SEND_INPUT_EVENT: {
- sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
- return;
- }
- case MSG_TIMEOUT_INPUT_EVENT: {
- finishedInputEvent(msg.arg1, false, true);
- return;
- }
- case MSG_FLUSH_INPUT_EVENT: {
- finishedInputEvent(msg.arg1, false, false);
- return;
- }
- }
- }
- }
-
- private final class TvInputEventSender extends InputEventSender {
- public TvInputEventSender(InputChannel inputChannel, Looper looper) {
- super(inputChannel, looper);
- }
-
- @Override
- public void onInputEventFinished(int seq, boolean handled) {
- finishedInputEvent(seq, handled, false);
- }
- }
-
- private final class PendingEvent implements Runnable {
- public InputEvent mEvent;
- public Object mToken;
- public FinishedInputEventCallback mCallback;
- public Handler mHandler;
- public boolean mHandled;
-
- public void recycle() {
- mEvent = null;
- mToken = null;
- mCallback = null;
- mHandler = null;
- mHandled = false;
- }
-
- @Override
- public void run() {
- mCallback.onFinishedInputEvent(mToken, mHandled);
-
- synchronized (mHandler) {
- recyclePendingEventLocked(this);
- }
- }
- }
- }
-}
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
deleted file mode 100644
index 03d24db..0000000
--- a/core/java/android/tv/TvInputService.java
+++ /dev/null
@@ -1,598 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.tv.TvInputManager.Session;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.InputChannel;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.InputEventReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.SomeArgs;
-
-/**
- * A base class for implementing television input service.
- */
-public abstract class TvInputService extends Service {
- // STOPSHIP: Turn debugging off.
- private static final boolean DEBUG = true;
- private static final String TAG = "TvInputService";
-
- /**
- * This is the interface name that a service implementing a TV input should say that it support
- * -- that is, this is the action it uses for its intent filter. To be supported, the service
- * must also require the {@link android.Manifest.permission#BIND_TV_INPUT} permission so that
- * other applications cannot abuse it.
- */
- public static final String SERVICE_INTERFACE = "android.tv.TvInputService";
-
- private String mId;
- private final Handler mHandler = new ServiceHandler();
- private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
- new RemoteCallbackList<ITvInputServiceCallback>();
- private boolean mAvailable;
-
- @Override
- public void onCreate() {
- super.onCreate();
- mId = TvInputInfo.generateInputIdForComponentName(
- new ComponentName(getPackageName(), getClass().getName()));
- }
-
- @Override
- public final IBinder onBind(Intent intent) {
- return new ITvInputService.Stub() {
- @Override
- public void registerCallback(ITvInputServiceCallback cb) {
- if (cb != null) {
- mCallbacks.register(cb);
- // The first time a callback is registered, the service needs to report its
- // availability status so that the system can know its initial value.
- try {
- cb.onAvailabilityChanged(mId, mAvailable);
- } catch (RemoteException e) {
- Log.e(TAG, "error in onAvailabilityChanged", e);
- }
- }
- }
-
- @Override
- public void unregisterCallback(ITvInputServiceCallback cb) {
- if (cb != null) {
- mCallbacks.unregister(cb);
- }
- }
-
- @Override
- public void createSession(InputChannel channel, ITvInputSessionCallback cb) {
- if (channel == null) {
- Log.w(TAG, "Creating session without input channel");
- }
- if (cb == null) {
- return;
- }
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = channel;
- args.arg2 = cb;
- mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
- }
- };
- }
-
- /**
- * Convenience method to notify an availability change of this TV input service.
- *
- * @param available {@code true} if the input service is available to show TV programs.
- */
- public final void setAvailable(boolean available) {
- if (available != mAvailable) {
- mAvailable = available;
- mHandler.obtainMessage(ServiceHandler.DO_BROADCAST_AVAILABILITY_CHANGE, available)
- .sendToTarget();
- }
- }
-
- /**
- * Get the number of callbacks that are registered.
- *
- * @hide
- */
- @VisibleForTesting
- public final int getRegisteredCallbackCount() {
- return mCallbacks.getRegisteredCallbackCount();
- }
-
- /**
- * Returns a concrete implementation of {@link TvInputSessionImpl}.
- * <p>
- * May return {@code null} if this TV input service fails to create a session for some reason.
- * </p>
- */
- public abstract TvInputSessionImpl onCreateSession();
-
- /**
- * Base class for derived classes to implement to provide {@link TvInputManager.Session}.
- */
- public abstract class TvInputSessionImpl implements KeyEvent.Callback {
- private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
- private final WindowManager mWindowManager;
- private WindowManager.LayoutParams mWindowParams;
- private Surface mSurface;
- private View mOverlayView;
- private boolean mOverlayViewEnabled;
- private IBinder mWindowToken;
- private Rect mOverlayFrame;
- private ITvInputSessionCallback mSessionCallback;
-
- public TvInputSessionImpl() {
- mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- }
-
- /**
- * Enables or disables the overlay view. By default, the overlay view is disabled. Must be
- * called explicitly after the session is created to enable the overlay view.
- *
- * @param enable {@code true} if you want to enable the overlay view. {@code false}
- * otherwise.
- */
- public void setOverlayViewEnabled(final boolean enable) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (enable == mOverlayViewEnabled) {
- return;
- }
- mOverlayViewEnabled = enable;
- if (enable) {
- if (mWindowToken != null) {
- createOverlayView(mWindowToken, mOverlayFrame);
- }
- } else {
- removeOverlayView(false);
- }
- }
- });
- }
-
- /**
- * Dispatches an event to the application using this session.
- *
- * @param eventType The type of the event.
- * @param eventArgs Optional arguments of the event.
- * @hide
- */
- public void dispatchSessionEvent(final String eventType, final Bundle eventArgs) {
- if (eventType == null) {
- throw new IllegalArgumentException("eventType should not be null.");
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- if (DEBUG) Log.d(TAG, "dispatchSessionEvent(" + eventType + ")");
- mSessionCallback.onSessionEvent(eventType, eventArgs);
- } catch (RemoteException e) {
- Log.w(TAG, "error in sending event (event=" + eventType + ")");
- }
- }
- });
- }
-
- /**
- * Sends the change on the size of the video. This is expected to be called at the
- * beginning of the playback and later when the size has been changed.
- *
- * @param width The width of the video.
- * @param height The height of the video.
- * @hide
- */
- public void dispatchVideoSizeChanged(final int width, final int height) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- try {
- if (DEBUG) Log.d(TAG, "dispatchVideoSizeChanged");
- mSessionCallback.onVideoSizeChanged(width, height);
- } catch (RemoteException e) {
- Log.w(TAG, "error in dispatchVideoSizeChanged");
- }
- }
- });
- }
-
- /**
- * Called when the session is released.
- */
- public abstract void onRelease();
-
- /**
- * Sets the {@link Surface} for the current input session on which the TV input renders
- * video.
- *
- * @param surface {@link Surface} an application passes to this TV input session.
- * @return {@code true} if the surface was set, {@code false} otherwise.
- */
- public abstract boolean onSetSurface(Surface surface);
-
- /**
- * Sets the relative volume of the current TV input session to handle the change of audio
- * focus by setting.
- *
- * @param volume Volume scale from 0.0 to 1.0.
- */
- public abstract void onSetVolume(float volume);
-
- /**
- * Tunes to a given channel.
- *
- * @param channelUri The URI of the channel.
- * @return {@code true} the tuning was successful, {@code false} otherwise.
- */
- public abstract boolean onTune(Uri channelUri);
-
- /**
- * Called when an application requests to create an overlay view. Each session
- * implementation can override this method and return its own view.
- *
- * @return a view attached to the overlay window
- */
- public View onCreateOverlayView() {
- return null;
- }
-
- /**
- * Default implementation of {@link android.view.KeyEvent.Callback#onKeyDown(int, KeyEvent)
- * KeyEvent.Callback.onKeyDown()}: always returns false (doesn't handle the event).
- * <p>
- * Override this to intercept key down events before they are processed by the application.
- * If you return true, the application will not process the event itself. If you return
- * false, the normal application processing will occur as if the TV input had not seen the
- * event at all.
- *
- * @param keyCode The value in event.getKeyCode().
- * @param event Description of the key event.
- * @return If you handled the event, return {@code true}. If you want to allow the event to
- * be handled by the next receiver, return {@code false}.
- */
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- return false;
- }
-
- /**
- * Default implementation of
- * {@link android.view.KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
- * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle the event).
- * <p>
- * Override this to intercept key long press events before they are processed by the
- * application. If you return true, the application will not process the event itself. If
- * you return false, the normal application processing will occur as if the TV input had not
- * seen the event at all.
- *
- * @param keyCode The value in event.getKeyCode().
- * @param event Description of the key event.
- * @return If you handled the event, return {@code true}. If you want to allow the event to
- * be handled by the next receiver, return {@code false}.
- */
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- return false;
- }
-
- /**
- * Default implementation of
- * {@link android.view.KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
- * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle the event).
- * <p>
- * Override this to intercept special key multiple events before they are processed by the
- * application. If you return true, the application will not itself process the event. If
- * you return false, the normal application processing will occur as if the TV input had not
- * seen the event at all.
- *
- * @param keyCode The value in event.getKeyCode().
- * @param count The number of times the action was made.
- * @param event Description of the key event.
- * @return If you handled the event, return {@code true}. If you want to allow the event to
- * be handled by the next receiver, return {@code false}.
- */
- @Override
- public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
- return false;
- }
-
- /**
- * Default implementation of {@link android.view.KeyEvent.Callback#onKeyUp(int, KeyEvent)
- * KeyEvent.Callback.onKeyUp()}: always returns false (doesn't handle the event).
- * <p>
- * Override this to intercept key up events before they are processed by the application. If
- * you return true, the application will not itself process the event. If you return false,
- * the normal application processing will occur as if the TV input had not seen the event at
- * all.
- *
- * @param keyCode The value in event.getKeyCode().
- * @param event Description of the key event.
- * @return If you handled the event, return {@code true}. If you want to allow the event to
- * be handled by the next receiver, return {@code false}.
- */
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- return false;
- }
-
- /**
- * Implement this method to handle touch screen motion events on the current input session.
- *
- * @param event The motion event being received.
- * @return If you handled the event, return {@code true}. If you want to allow the event to
- * be handled by the next receiver, return {@code false}.
- * @see View#onTouchEvent
- */
- public boolean onTouchEvent(MotionEvent event) {
- return false;
- }
-
- /**
- * Implement this method to handle trackball events on the current input session.
- *
- * @param event The motion event being received.
- * @return If you handled the event, return {@code true}. If you want to allow the event to
- * be handled by the next receiver, return {@code false}.
- * @see View#onTrackballEvent
- */
- public boolean onTrackballEvent(MotionEvent event) {
- return false;
- }
-
- /**
- * Implement this method to handle generic motion events on the current input session.
- *
- * @param event The motion event being received.
- * @return If you handled the event, return {@code true}. If you want to allow the event to
- * be handled by the next receiver, return {@code false}.
- * @see View#onGenericMotionEvent
- */
- public boolean onGenericMotionEvent(MotionEvent event) {
- return false;
- }
-
- /**
- * This method is called when the application would like to stop using the current input
- * session.
- */
- void release() {
- onRelease();
- if (mSurface != null) {
- mSurface.release();
- mSurface = null;
- }
- removeOverlayView(true);
- }
-
- /**
- * Calls {@link #onSetSurface}.
- */
- void setSurface(Surface surface) {
- onSetSurface(surface);
- if (mSurface != null) {
- mSurface.release();
- }
- mSurface = surface;
- // TODO: Handle failure.
- }
-
- /**
- * Calls {@link #onSetVolume}.
- */
- void setVolume(float volume) {
- onSetVolume(volume);
- }
-
- /**
- * Calls {@link #onTune}.
- */
- void tune(Uri channelUri) {
- onTune(channelUri);
- // TODO: Handle failure.
- }
-
- /**
- * Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach
- * to the overlay window.
- *
- * @param windowToken A window token of an application.
- * @param frame A position of the overlay view.
- */
- void createOverlayView(IBinder windowToken, Rect frame) {
- if (mOverlayView != null) {
- mWindowManager.removeView(mOverlayView);
- mOverlayView = null;
- }
- if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")");
- mWindowToken = windowToken;
- mOverlayFrame = frame;
- if (!mOverlayViewEnabled) {
- return;
- }
- mOverlayView = onCreateOverlayView();
- if (mOverlayView == null) {
- return;
- }
- // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create
- // an overlay window above the media window but below the application window.
- int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
- // We make the overlay view non-focusable and non-touchable so that
- // the application that owns the window token can decide whether to consume or
- // dispatch the input events.
- int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- mWindowParams = new WindowManager.LayoutParams(
- frame.right - frame.left, frame.bottom - frame.top,
- frame.left, frame.top, type, flag, PixelFormat.TRANSPARENT);
- mWindowParams.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
- mWindowParams.gravity = Gravity.START | Gravity.TOP;
- mWindowParams.token = windowToken;
- mWindowManager.addView(mOverlayView, mWindowParams);
- }
-
- /**
- * Relayouts the current overlay view.
- *
- * @param frame A new position of the overlay view.
- */
- void relayoutOverlayView(Rect frame) {
- if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")");
- mOverlayFrame = frame;
- if (!mOverlayViewEnabled || mOverlayView == null) {
- return;
- }
- mWindowParams.x = frame.left;
- mWindowParams.y = frame.top;
- mWindowParams.width = frame.right - frame.left;
- mWindowParams.height = frame.bottom - frame.top;
- mWindowManager.updateViewLayout(mOverlayView, mWindowParams);
- }
-
- /**
- * Removes the current overlay view.
- */
- void removeOverlayView(boolean clearWindowToken) {
- if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayView + ")");
- if (clearWindowToken) {
- mWindowToken = null;
- mOverlayFrame = null;
- }
- if (mOverlayView != null) {
- mWindowManager.removeView(mOverlayView);
- mOverlayView = null;
- mWindowParams = null;
- }
- }
-
- /**
- * Takes care of dispatching incoming input events and tells whether the event was handled.
- */
- int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
- if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
- if (event instanceof KeyEvent) {
- if (((KeyEvent) event).dispatch(this, mDispatcherState, this)) {
- return Session.DISPATCH_HANDLED;
- }
- } else if (event instanceof MotionEvent) {
- MotionEvent motionEvent = (MotionEvent) event;
- final int source = motionEvent.getSource();
- if (motionEvent.isTouchEvent()) {
- if (onTouchEvent(motionEvent)) {
- return Session.DISPATCH_HANDLED;
- }
- } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- if (onTrackballEvent(motionEvent)) {
- return Session.DISPATCH_HANDLED;
- }
- } else {
- if (onGenericMotionEvent(motionEvent)) {
- return Session.DISPATCH_HANDLED;
- }
- }
- }
- if (mOverlayView == null || !mOverlayView.isAttachedToWindow()) {
- return Session.DISPATCH_NOT_HANDLED;
- }
- if (!mOverlayView.hasWindowFocus()) {
- mOverlayView.getViewRootImpl().windowFocusChanged(true, true);
- }
- mOverlayView.getViewRootImpl().dispatchInputEvent(event, receiver);
- return Session.DISPATCH_IN_PROGRESS;
- }
-
- private void setSessionCallback(ITvInputSessionCallback callback) {
- mSessionCallback = callback;
- }
- }
-
- private final class ServiceHandler extends Handler {
- private static final int DO_CREATE_SESSION = 1;
- private static final int DO_BROADCAST_AVAILABILITY_CHANGE = 2;
-
- @Override
- public final void handleMessage(Message msg) {
- switch (msg.what) {
- case DO_CREATE_SESSION: {
- SomeArgs args = (SomeArgs) msg.obj;
- InputChannel channel = (InputChannel) args.arg1;
- ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
- try {
- TvInputSessionImpl sessionImpl = onCreateSession();
- if (sessionImpl == null) {
- // Failed to create a session.
- cb.onSessionCreated(null);
- } else {
- sessionImpl.setSessionCallback(cb);
- ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
- sessionImpl, channel);
- cb.onSessionCreated(stub);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "error in onSessionCreated");
- }
- args.recycle();
- return;
- }
- case DO_BROADCAST_AVAILABILITY_CHANGE: {
- boolean isAvailable = (Boolean) msg.obj;
- int n = mCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Unexpected exception", e);
- } finally {
- mCallbacks.finishBroadcast();
- }
- return;
- }
- default: {
- Log.w(TAG, "Unhandled message code: " + msg.what);
- return;
- }
- }
- }
- }
-}
diff --git a/core/java/android/tv/TvStreamConfig.aidl b/core/java/android/tv/TvStreamConfig.aidl
deleted file mode 100644
index 4d0add4..0000000
--- a/core/java/android/tv/TvStreamConfig.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-parcelable TvStreamConfig;
\ No newline at end of file
diff --git a/core/java/android/tv/TvStreamConfig.java b/core/java/android/tv/TvStreamConfig.java
deleted file mode 100644
index 03e63b1..0000000
--- a/core/java/android/tv/TvStreamConfig.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-/**
- * @hide
- */
-public class TvStreamConfig implements Parcelable {
- static final String TAG = TvStreamConfig.class.getSimpleName();
-
- public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1;
- public final static int STREAM_TYPE_BUFFER_PRODUCER = 2;
-
- private int mStreamId;
- private int mType;
- // TODO: Revisit if max widht/height really make sense.
- private int mMaxWidth;
- private int mMaxHeight;
- /**
- * Generations are incremented once framework receives STREAM_CONFIGURATION_CHANGED event from
- * HAL module. Framework should throw away outdated configurations and get new configurations
- * via tv_input_device::get_stream_configurations().
- */
- private int mGeneration;
-
- public static final Parcelable.Creator<TvStreamConfig> CREATOR =
- new Parcelable.Creator<TvStreamConfig>() {
- @Override
- public TvStreamConfig createFromParcel(Parcel source) {
- try {
- return new Builder().
- streamId(source.readInt()).
- type(source.readInt()).
- maxWidth(source.readInt()).
- maxHeight(source.readInt()).
- generation(source.readInt()).build();
- } catch (Exception e) {
- Log.e(TAG, "Exception creating TvStreamConfig from parcel", e);
- return null;
- }
- }
-
- @Override
- public TvStreamConfig[] newArray(int size) {
- return new TvStreamConfig[size];
- }
- };
-
- private TvStreamConfig() {}
-
- public int getStreamId() {
- return mStreamId;
- }
-
- public int getType() {
- return mType;
- }
-
- public int getMaxWidth() {
- return mMaxWidth;
- }
-
- public int getMaxHeight() {
- return mMaxHeight;
- }
-
- public int getGeneration() {
- return mGeneration;
- }
-
- // Parcelable
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mStreamId);
- dest.writeInt(mType);
- dest.writeInt(mMaxWidth);
- dest.writeInt(mMaxHeight);
- dest.writeInt(mGeneration);
- }
-
- /**
- * A helper class for creating a TvStreamConfig object.
- */
- public static final class Builder {
- private Integer mStreamId;
- private Integer mType;
- private Integer mMaxWidth;
- private Integer mMaxHeight;
- private Integer mGeneration;
-
- public Builder() {
- }
-
- public Builder streamId(int streamId) {
- mStreamId = streamId;
- return this;
- }
-
- public Builder type(int type) {
- mType = type;
- return this;
- }
-
- public Builder maxWidth(int maxWidth) {
- mMaxWidth = maxWidth;
- return this;
- }
-
- public Builder maxHeight(int maxHeight) {
- mMaxHeight = maxHeight;
- return this;
- }
-
- public Builder generation(int generation) {
- mGeneration = generation;
- return this;
- }
-
- public TvStreamConfig build() {
- if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null
- || mGeneration == null) {
- throw new UnsupportedOperationException();
- }
-
- TvStreamConfig config = new TvStreamConfig();
- config.mStreamId = mStreamId;
- config.mType = mType;
- config.mMaxWidth = mMaxWidth;
- config.mMaxHeight = mMaxHeight;
- config.mGeneration = mGeneration;
- return config;
- }
- }
-}
\ No newline at end of file
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
deleted file mode 100644
index 2d31701..0000000
--- a/core/java/android/tv/TvView.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.tv;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.text.TextUtils;
-import android.tv.TvInputManager.Session;
-import android.tv.TvInputManager.Session.FinishedInputEventCallback;
-import android.tv.TvInputManager.SessionCallback;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.ViewRootImpl;
-
-/**
- * View playing TV
- */
-public class TvView extends SurfaceView {
- // STOPSHIP: Turn debugging off.
- private static final boolean DEBUG = true;
- private static final String TAG = "TvView";
-
- private final Handler mHandler = new Handler();
- private TvInputManager.Session mSession;
- private Surface mSurface;
- private boolean mOverlayViewCreated;
- private Rect mOverlayViewFrame;
- private final TvInputManager mTvInputManager;
- private SessionCallback mSessionCallback;
- private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
-
- private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format + ", width=" + width
- + ", height=" + height + ")");
- if (holder.getSurface() == mSurface) {
- return;
- }
- mSurface = holder.getSurface();
- setSessionSurface(mSurface);
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- mSurface = holder.getSurface();
- setSessionSurface(mSurface);
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- mSurface = null;
- setSessionSurface(null);
- }
- };
-
- private final FinishedInputEventCallback mFinishedInputEventCallback =
- new FinishedInputEventCallback() {
- @Override
- public void onFinishedInputEvent(Object token, boolean handled) {
- if (DEBUG) {
- Log.d(TAG, "onFinishedInputEvent(token=" + token + ", handled=" + handled + ")");
- }
- if (handled) {
- return;
- }
- // TODO: Re-order unhandled events.
- InputEvent event = (InputEvent) token;
- if (dispatchUnhandledInputEvent(event)) {
- return;
- }
- ViewRootImpl viewRootImpl = getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.dispatchUnhandledInputEvent(event);
- }
- }
- };
-
- public TvView(Context context) {
- this(context, null, 0);
- }
-
- public TvView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- getHolder().addCallback(mSurfaceHolderCallback);
- mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
- }
-
- /**
- * Binds a TV input to this view. {@link SessionCallback#onSessionCreated} will be
- * called to send the result of this binding with {@link TvInputManager.Session}.
- * If a TV input is already bound, the input will be unbound from this view and its session
- * will be released.
- *
- * @param inputId the id of TV input which will be bound to this view.
- * @param callback called when TV input is bound. The callback sends
- * {@link TvInputManager.Session}
- * @throws IllegalArgumentException if any of the arguments is {@code null}.
- */
- public void bindTvInput(String inputId, SessionCallback callback) {
- if (TextUtils.isEmpty(inputId)) {
- throw new IllegalArgumentException("inputId cannot be null or an empty string");
- }
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null");
- }
- if (mSession != null) {
- release();
- }
- // When bindTvInput is called multiple times before the callback is called,
- // only the callback of the last bindTvInput call will be actually called back.
- // The previous callbacks will be ignored. For the logic, mSessionCallback
- // is newly assigned for every bindTvInput call and compared with
- // MySessionCreateCallback.this.
- mSessionCallback = new MySessionCallback(callback);
- mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
- }
-
- /**
- * Unbinds a TV input currently bound. Its corresponding {@link TvInputManager.Session}
- * is released.
- */
- public void unbindTvInput() {
- if (mSession != null) {
- release();
- }
- mSessionCallback = null;
- }
-
- /**
- * Dispatches an unhandled input event to the next receiver.
- * <p>
- * Except system keys, TvView always consumes input events in the normal flow. This is called
- * asynchronously from where the event is dispatched. It gives the host application a chance to
- * dispatch the unhandled input events.
- *
- * @param event The input event.
- * @return {@code true} if the event was handled by the view, {@code false} otherwise.
- */
- public boolean dispatchUnhandledInputEvent(InputEvent event) {
- if (mOnUnhandledInputEventListener != null) {
- if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) {
- return true;
- }
- }
- return onUnhandledInputEvent(event);
- }
-
- /**
- * Called when an unhandled input event was also not handled by the user provided callback. This
- * is the last chance to handle the unhandled input event in the TvView.
- *
- * @param event The input event.
- * @return If you handled the event, return {@code true}. If you want to allow the event to be
- * handled by the next receiver, return {@code false}.
- */
- public boolean onUnhandledInputEvent(InputEvent event) {
- return false;
- }
-
- /**
- * Registers a callback to be invoked when an input event was not handled by the bound TV input.
- *
- * @param listener The callback to invoke when the unhandled input event was received.
- */
- public void setOnUnhandledInputEventListener(OnUnhandledInputEventListener listener) {
- mOnUnhandledInputEventListener = listener;
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (super.dispatchKeyEvent(event)) {
- return true;
- }
- if (DEBUG) Log.d(TAG, "dispatchKeyEvent(" + event + ")");
- if (mSession == null) {
- return false;
- }
- InputEvent copiedEvent = event.copy();
- int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
- mHandler);
- return ret != Session.DISPATCH_NOT_HANDLED;
- }
-
- @Override
- public boolean dispatchTouchEvent(MotionEvent event) {
- if (super.dispatchTouchEvent(event)) {
- return true;
- }
- if (DEBUG) Log.d(TAG, "dispatchTouchEvent(" + event + ")");
- if (mSession == null) {
- return false;
- }
- InputEvent copiedEvent = event.copy();
- int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
- mHandler);
- return ret != Session.DISPATCH_NOT_HANDLED;
- }
-
- @Override
- public boolean dispatchTrackballEvent(MotionEvent event) {
- if (super.dispatchTrackballEvent(event)) {
- return true;
- }
- if (DEBUG) Log.d(TAG, "dispatchTrackballEvent(" + event + ")");
- if (mSession == null) {
- return false;
- }
- InputEvent copiedEvent = event.copy();
- int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
- mHandler);
- return ret != Session.DISPATCH_NOT_HANDLED;
- }
-
- @Override
- public boolean dispatchGenericMotionEvent(MotionEvent event) {
- if (super.dispatchGenericMotionEvent(event)) {
- return true;
- }
- if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent(" + event + ")");
- if (mSession == null) {
- return false;
- }
- InputEvent copiedEvent = event.copy();
- int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
- mHandler);
- return ret != Session.DISPATCH_NOT_HANDLED;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- createSessionOverlayView();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- removeSessionOverlayView();
- super.onDetachedFromWindow();
- }
-
- /** @hide */
- @Override
- protected void updateWindow(boolean force, boolean redrawNeeded) {
- super.updateWindow(force, redrawNeeded);
- relayoutSessionOverlayView();
- }
-
- private void release() {
- setSessionSurface(null);
- removeSessionOverlayView();
- mSession.release();
- mSession = null;
- }
-
- private void setSessionSurface(Surface surface) {
- if (mSession == null) {
- return;
- }
- mSession.setSurface(surface);
- }
-
- private void createSessionOverlayView() {
- if (mSession == null || !isAttachedToWindow()
- || mOverlayViewCreated) {
- return;
- }
- mOverlayViewFrame = getViewFrameOnScreen();
- mSession.createOverlayView(this, mOverlayViewFrame);
- mOverlayViewCreated = true;
- }
-
- private void removeSessionOverlayView() {
- if (mSession == null || !mOverlayViewCreated) {
- return;
- }
- mSession.removeOverlayView();
- mOverlayViewCreated = false;
- mOverlayViewFrame = null;
- }
-
- private void relayoutSessionOverlayView() {
- if (mSession == null || !isAttachedToWindow()
- || !mOverlayViewCreated) {
- return;
- }
- Rect viewFrame = getViewFrameOnScreen();
- if (viewFrame.equals(mOverlayViewFrame)) {
- return;
- }
- mSession.relayoutOverlayView(viewFrame);
- mOverlayViewFrame = viewFrame;
- }
-
- private Rect getViewFrameOnScreen() {
- int[] location = new int[2];
- getLocationOnScreen(location);
- return new Rect(location[0], location[1],
- location[0] + getWidth(), location[1] + getHeight());
- }
-
- /**
- * Interface definition for a callback to be invoked when the unhandled input event is received.
- */
- public interface OnUnhandledInputEventListener {
- /**
- * Called when an input event was not handled by the bound TV input.
- * <p>
- * This is called asynchronously from where the event is dispatched. It gives the host
- * application a chance to handle the unhandled input events.
- *
- * @param event The input event.
- * @return If you handled the event, return {@code true}. If you want to allow the event to
- * be handled by the next receiver, return {@code false}.
- */
- boolean onUnhandledInputEvent(InputEvent event);
- }
-
- private class MySessionCallback extends SessionCallback {
- final SessionCallback mExternalCallback;
-
- MySessionCallback(SessionCallback externalCallback) {
- mExternalCallback = externalCallback;
- }
-
- @Override
- public void onSessionCreated(Session session) {
- if (this != mSessionCallback) {
- // This callback is obsolete.
- if (session != null) {
- session.release();
- }
- return;
- }
- mSession = session;
- if (session != null) {
- // mSurface may not be ready yet as soon as starting an application.
- // In the case, we don't send Session.setSurface(null) unnecessarily.
- // setSessionSurface will be called in surfaceCreated.
- if (mSurface != null) {
- setSessionSurface(mSurface);
- }
- createSessionOverlayView();
- }
- if (mExternalCallback != null) {
- mExternalCallback.onSessionCreated(session);
- }
- }
-
- @Override
- public void onSessionReleased(Session session) {
- mSession = null;
- if (mExternalCallback != null) {
- mExternalCallback.onSessionReleased(session);
- }
- }
-
- @Override
- public void onVideoSizeChanged(Session session, int width, int height) {
- if (DEBUG) {
- Log.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
- }
- if (mExternalCallback != null) {
- mExternalCallback.onVideoSizeChanged(session, width, height);
- }
- }
-
- @Override
- public void onSessionEvent(TvInputManager.Session session, String eventType,
- Bundle eventArgs) {
- if (mExternalCallback != null) {
- mExternalCallback.onSessionEvent(session, eventType, eventArgs);
- }
- }
- }
-}
diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java
index d7e8cf0..3907e77 100644
--- a/core/java/android/util/Range.java
+++ b/core/java/android/util/Range.java
@@ -97,6 +97,27 @@
}
/**
+ * Checks if the {@code value} is within the bounds of this range.
+ *
+ * <p>A value is considered to be within this range if it's {@code >=} then
+ * the lower endpoint <i>and</i> {@code <=} to the upper endpoint (using the {@link Comparable}
+ * interface.</p>
+ *
+ * @param value a non-{@code null} {@code T} reference
+ * @return {@code true} if the value is within this inclusive range, {@code false} otherwise
+ *
+ * @throws NullPointerException if {@code value} was {@code null}
+ */
+ public boolean inRange(T value) {
+ checkNotNull(value, "value must not be null");
+
+ boolean gteLower = value.compareTo(mLower) >= 0;
+ boolean lteUpper = value.compareTo(mUpper) <= 0;
+
+ return gteLower && lteUpper;
+ }
+
+ /**
* Compare two ranges for equality.
*
* <p>A range is considered equal if and only if both the lower and upper endpoints
@@ -105,16 +126,13 @@
* @return {@code true} if the ranges are equal, {@code false} otherwise
*/
@Override
- public boolean equals(final Object obj) {
+ public boolean equals(Object obj) {
if (obj == null) {
return false;
- }
- if (this == obj) {
+ } else if (this == obj) {
return true;
- }
- if (obj instanceof Range) {
+ } else if (obj instanceof Range) {
@SuppressWarnings("rawtypes")
- final
Range other = (Range) obj;
return mLower.equals(other.mLower) && mUpper.equals(other.mUpper);
}
diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java
index 8d4c67f..9952859 100644
--- a/core/java/android/util/Rational.java
+++ b/core/java/android/util/Rational.java
@@ -15,29 +15,88 @@
*/
package android.util;
+import static com.android.internal.util.Preconditions.*;
+
+import java.io.IOException;
+import java.io.InvalidObjectException;
+
/**
* <p>An immutable data type representation a rational number.</p>
*
* <p>Contains a pair of {@code int}s representing the numerator and denominator of a
* Rational number. </p>
*/
-public final class Rational {
+public final class Rational extends Number implements Comparable<Rational> {
+ /**
+ * Constant for the <em>Not-a-Number (NaN)</em> value of the {@code Rational} type.
+ *
+ * <p>A {@code NaN} value is considered to be equal to itself (that is {@code NaN.equals(NaN)}
+ * will return {@code true}; it is always greater than any non-{@code NaN} value (that is
+ * {@code NaN.compareTo(notNaN)} will return a number greater than {@code 0}).</p>
+ *
+ * <p>Equivalent to constructing a new rational with both the numerator and denominator
+ * equal to {@code 0}.</p>
+ */
+ public static final Rational NaN = new Rational(0, 0);
+
+ /**
+ * Constant for the positive infinity value of the {@code Rational} type.
+ *
+ * <p>Equivalent to constructing a new rational with a positive numerator and a denominator
+ * equal to {@code 0}.</p>
+ */
+ public static final Rational POSITIVE_INFINITY = new Rational(1, 0);
+
+ /**
+ * Constant for the negative infinity value of the {@code Rational} type.
+ *
+ * <p>Equivalent to constructing a new rational with a negative numerator and a denominator
+ * equal to {@code 0}.</p>
+ */
+ public static final Rational NEGATIVE_INFINITY = new Rational(-1, 0);
+
+ /**
+ * Constant for the zero value of the {@code Rational} type.
+ *
+ * <p>Equivalent to constructing a new rational with a numerator equal to {@code 0} and
+ * any non-zero denominator.</p>
+ */
+ public static final Rational ZERO = new Rational(0, 1);
+
+ /**
+ * Unique version number per class to be compliant with {@link java.io.Serializable}.
+ *
+ * <p>Increment each time the fields change in any way.</p>
+ */
+ private static final long serialVersionUID = 1L;
+
+ /*
+ * Do not change the order of these fields or add new instance fields to maintain the
+ * Serializable compatibility across API revisions.
+ */
private final int mNumerator;
private final int mDenominator;
/**
- * <p>Create a Rational with a given numerator and denominator.</p>
+ * <p>Create a {@code Rational} with a given numerator and denominator.</p>
*
* <p>The signs of the numerator and the denominator may be flipped such that the denominator
- * is always positive.</p>
+ * is always positive. Both the numerator and denominator will be converted to their reduced
+ * forms (see {@link #equals} for more details).</p>
*
- * <p>A rational value with a 0-denominator may be constructed, but will have similar semantics
- * as float {@code NaN} and {@code INF} values. For {@code NaN},
- * both {@link #getNumerator} and {@link #getDenominator} functions will return 0. For
- * positive or negative {@code INF}, only the {@link #getDenominator} will return 0.</p>
+ * <p>For example,
+ * <ul>
+ * <li>a rational of {@code 2/4} will be reduced to {@code 1/2}.
+ * <li>a rational of {@code 1/-1} will be flipped to {@code -1/1}
+ * <li>a rational of {@code 5/0} will be reduced to {@code 1/0}
+ * <li>a rational of {@code 0/5} will be reduced to {@code 0/1}
+ * </ul>
+ * </p>
*
* @param numerator the numerator of the rational
* @param denominator the denominator of the rational
+ *
+ * @see #equals
*/
public Rational(int numerator, int denominator) {
@@ -46,32 +105,100 @@
denominator = -denominator;
}
- mNumerator = numerator;
- mDenominator = denominator;
+ // Convert to reduced form
+ if (denominator == 0 && numerator > 0) {
+ mNumerator = 1; // +Inf
+ mDenominator = 0;
+ } else if (denominator == 0 && numerator < 0) {
+ mNumerator = -1; // -Inf
+ mDenominator = 0;
+ } else if (denominator == 0 && numerator == 0) {
+ mNumerator = 0; // NaN
+ mDenominator = 0;
+ } else if (numerator == 0) {
+ mNumerator = 0;
+ mDenominator = 1;
+ } else {
+ int gcd = gcd(numerator, denominator);
+
+ mNumerator = numerator / gcd;
+ mDenominator = denominator / gcd;
+ }
}
/**
* Gets the numerator of the rational.
+ *
+ * <p>The numerator will always return {@code 1} if this rational represents
+ * infinity (that is, the denominator is {@code 0}).</p>
*/
public int getNumerator() {
- if (mDenominator == 0) {
- return 0;
- }
return mNumerator;
}
/**
* Gets the denominator of the rational
+ *
+ * <p>The denominator may return {@code 0}, in which case the rational may represent
+ * positive infinity (if the numerator was positive), negative infinity (if the numerator
+ * was negative), or {@code NaN} (if the numerator was {@code 0}).</p>
+ *
+ * <p>The denominator will always return {@code 1} if the numerator is {@code 0}.
*/
public int getDenominator() {
return mDenominator;
}
- private boolean isNaN() {
+ /**
+ * Indicates whether this rational is a <em>Not-a-Number (NaN)</em> value.
+ *
+ * <p>A {@code NaN} value occurs when both the numerator and the denominator are {@code 0}.</p>
+ *
+ * @return {@code true} if this rational is a <em>Not-a-Number (NaN)</em> value;
+ * {@code false} if this is a (potentially infinite) number value
+ */
+ public boolean isNaN() {
return mDenominator == 0 && mNumerator == 0;
}
- private boolean isInf() {
+ /**
+ * Indicates whether this rational represents an infinite value.
+ *
+ * <p>An infinite value occurs when the denominator is {@code 0} (but the numerator is not).</p>
+ *
+ * @return {@code true} if this rational is a (positive or negative) infinite value;
+ * {@code false} if this is a finite number value (or {@code NaN})
+ */
+ public boolean isInfinite() {
+ return mNumerator != 0 && mDenominator == 0;
+ }
+
+ /**
+ * Indicates whether this rational represents a finite value.
+ *
+ * <p>A finite value occurs when the denominator is not {@code 0}; in other words
+ * the rational is neither infinity or {@code NaN}.</p>
+ *
+ * @return {@code true} if this rational is a (positive or negative) infinite value;
+ * {@code false} if this is a finite number value (or {@code NaN})
+ */
+ public boolean isFinite() {
+ return mDenominator != 0;
+ }
+
+ /**
+ * Indicates whether this rational represents a zero value.
+ *
+ * <p>A zero value is a {@link #isFinite finite} rational with a numerator of {@code 0}.</p>
+ *
+ * @return {@code true} if this rational is finite zero value;
+ * {@code false} otherwise
+ */
+ public boolean isZero() {
+ return isFinite() && mNumerator == 0;
+ }
+
+ private boolean isPosInf() {
return mDenominator == 0 && mNumerator > 0;
}
@@ -82,12 +209,12 @@
/**
* <p>Compare this Rational to another object and see if they are equal.</p>
*
- * <p>A Rational object can only be equal to another Rational object (comparing against any other
- * type will return false).</p>
+ * <p>A Rational object can only be equal to another Rational object (comparing against any
+ * other type will return {@code false}).</p>
*
* <p>A Rational object is considered equal to another Rational object if and only if one of
- * the following holds</p>:
- * <ul><li>Both are NaN</li>
+ * the following holds:</p>
+ * <ul><li>Both are {@code NaN}</li>
* <li>Both are infinities of the same sign</li>
* <li>Both have the same numerator and denominator in their reduced form</li>
* </ul>
@@ -96,12 +223,12 @@
* denominator by their greatest common divisor.</p>
*
* <pre>{@code
- * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true
- * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false
- * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction
- * (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN)
- * (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity
- * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
+ * (new Rational(1, 2)).equals(new Rational(1, 2)) == true // trivially true
+ * (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false
+ * (new Rational(1, 2)).equals(new Rational(2, 4)) == true // true after reduction
+ * (new Rational(0, 0)).equals(new Rational(0, 0)) == true // NaN.equals(NaN)
+ * (new Rational(1, 0)).equals(new Rational(5, 0)) == true // both are +infinity
+ * (new Rational(1, 0)).equals(new Rational(-1, 0)) == false // +infinity != -infinity
* }</pre>
*
* @param obj a reference to another object
@@ -110,41 +237,31 @@
*/
@Override
public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- } else if (obj instanceof Rational) {
- Rational other = (Rational) obj;
- if (mDenominator == 0 || other.mDenominator == 0) {
- if (isNaN() && other.isNaN()) {
- return true;
- } else if (isInf() && other.isInf() || isNegInf() && other.isNegInf()) {
- return true;
- } else {
- return false;
- }
- } else if (mNumerator == other.mNumerator && mDenominator == other.mDenominator) {
- return true;
- } else {
- int thisGcd = gcd();
- int otherGcd = other.gcd();
-
- int thisNumerator = mNumerator / thisGcd;
- int thisDenominator = mDenominator / thisGcd;
-
- int otherNumerator = other.mNumerator / otherGcd;
- int otherDenominator = other.mDenominator / otherGcd;
-
- return (thisNumerator == otherNumerator && thisDenominator == otherDenominator);
- }
- }
- return false;
+ return obj instanceof Rational && equals((Rational) obj);
}
+ private boolean equals(Rational other) {
+ return (mNumerator == other.mNumerator && mDenominator == other.mDenominator);
+ }
+
+ /**
+ * Return a string representation of this rational, e.g. {@code "1/2"}.
+ *
+ * <p>The following rules of conversion apply:
+ * <ul>
+ * <li>{@code NaN} values will return {@code "NaN"}
+ * <li>Positive infinity values will return {@code "Infinity"}
+ * <li>Negative infinity values will return {@code "-Infinity"}
+ * <li>All other values will return {@code "numerator/denominator"} where {@code numerator}
+ * and {@code denominator} are substituted with the appropriate numerator and denominator
+ * values.
+ * </ul></p>
+ */
@Override
public String toString() {
if (isNaN()) {
return "NaN";
- } else if (isInf()) {
+ } else if (isPosInf()) {
return "Infinity";
} else if (isNegInf()) {
return "-Infinity";
@@ -160,7 +277,8 @@
* @hide
*/
public float toFloat() {
- return (float) mNumerator / (float) mDenominator;
+ // TODO: remove this duplicate function (used in CTS and the shim)
+ return floatValue();
}
/**
@@ -177,20 +295,24 @@
/**
* Calculates the greatest common divisor using Euclid's algorithm.
*
+ * <p><em>Visible for testing only.</em></p>
+ *
+ * @param numerator the numerator in a fraction
+ * @param denominator the denominator in a fraction
+ *
* @return An int value representing the gcd. Always positive.
* @hide
*/
- public int gcd() {
- /**
+ public static int gcd(int numerator, int denominator) {
+ /*
* Non-recursive implementation of Euclid's algorithm:
*
* gcd(a, 0) := a
* gcd(a, b) := gcd(b, a mod b)
*
*/
-
- int a = mNumerator;
- int b = mDenominator;
+ int a = numerator;
+ int b = denominator;
while (b != 0) {
int oldB = b;
@@ -201,4 +323,221 @@
return Math.abs(a);
}
+
+ /**
+ * Returns the value of the specified number as a {@code double}.
+ *
+ * <p>The {@code double} is calculated by converting both the numerator and denominator
+ * to a {@code double}; then returning the result of dividing the numerator by the
+ * denominator.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code double}.
+ */
+ @Override
+ public double doubleValue() {
+ double num = mNumerator;
+ double den = mDenominator;
+
+ return num / den;
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code float}.
+ *
+ * <p>The {@code float} is calculated by converting both the numerator and denominator
+ * to a {@code float}; then returning the result of dividing the numerator by the
+ * denominator.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code float}.
+ */
+ @Override
+ public float floatValue() {
+ float num = mNumerator;
+ float den = mDenominator;
+
+ return num / den;
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code int}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to an {@code int} value
+ * by dividing the numerator by the denominator; conversion for non-finite values happens
+ * identically to casting a floating point value to an {@code int}, in particular:
+ *
+ * <p>
+ * <ul>
+ * <li>Positive infinity saturates to the largest maximum integer
+ * {@link Integer#MAX_VALUE}</li>
+ * <li>Negative infinity saturates to the smallest maximum integer
+ * {@link Integer#MIN_VALUE}</li>
+ * <li><em>Not-A-Number (NaN)</em> returns {@code 0}.</li>
+ * </ul>
+ * </p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code int}.
+ */
+ @Override
+ public int intValue() {
+ // Mimic float to int conversion rules from JLS 5.1.3
+
+ if (isPosInf()) {
+ return Integer.MAX_VALUE;
+ } else if (isNegInf()) {
+ return Integer.MIN_VALUE;
+ } else if (isNaN()) {
+ return 0;
+ } else { // finite
+ return mNumerator / mDenominator;
+ }
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code long}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to an {@code long} value
+ * by dividing the numerator by the denominator; conversion for non-finite values happens
+ * identically to casting a floating point value to a {@code long}, in particular:
+ *
+ * <p>
+ * <ul>
+ * <li>Positive infinity saturates to the largest maximum long
+ * {@link Long#MAX_VALUE}</li>
+ * <li>Negative infinity saturates to the smallest maximum long
+ * {@link Long#MIN_VALUE}</li>
+ * <li><em>Not-A-Number (NaN)</em> returns {@code 0}.</li>
+ * </ul>
+ * </p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code long}.
+ */
+ @Override
+ public long longValue() {
+ // Mimic float to long conversion rules from JLS 5.1.3
+
+ if (isPosInf()) {
+ return Long.MAX_VALUE;
+ } else if (isNegInf()) {
+ return Long.MIN_VALUE;
+ } else if (isNaN()) {
+ return 0;
+ } else { // finite
+ return mNumerator / mDenominator;
+ }
+ }
+
+ /**
+ * Returns the value of the specified number as a {@code short}.
+ *
+ * <p>{@link #isInfinite Finite} rationals are converted to a {@code short} value
+ * identically to {@link #intValue}; the {@code int} result is then truncated to a
+ * {@code short} before returning the value.</p>
+ *
+ * @return the divided value of the numerator and denominator as a {@code short}.
+ */
+ @Override
+ public short shortValue() {
+ return (short) intValue();
+ }
+
+ /**
+ * Compare this rational to the specified rational to determine their natural order.
+ *
+ * <p>{@link #NaN} is considered to be equal to itself and greater than all other
+ * {@code Rational} values. Otherwise, if the objects are not {@link #equals equal}, then
+ * the following rules apply:</p>
+ *
+ * <ul>
+ * <li>Positive infinity is greater than any other finite number (or negative infinity)
+ * <li>Negative infinity is less than any other finite number (or positive infinity)
+ * <li>The finite number represented by this rational is checked numerically
+ * against the other finite number by converting both rationals to a common denominator multiple
+ * and comparing their numerators.
+ * </ul>
+ *
+ * @param another the rational to be compared
+ *
+ * @return a negative integer, zero, or a positive integer as this object is less than,
+ * equal to, or greater than the specified rational.
+ *
+ * @throws NullPointerException if {@code another} was {@code null}
+ */
+ @Override
+ public int compareTo(Rational another) {
+ checkNotNull(another, "another must not be null");
+
+ if (equals(another)) {
+ return 0;
+ } else if (isNaN()) { // NaN is greater than the other non-NaN value
+ return 1;
+ } else if (another.isNaN()) { // the other NaN is greater than this non-NaN value
+ return -1;
+ } else if (isPosInf() || another.isNegInf()) {
+ return 1; // positive infinity is greater than any non-NaN/non-posInf value
+ } else if (isNegInf() || another.isPosInf()) {
+ return -1; // negative infinity is less than any non-NaN/non-negInf value
+ }
+
+ // else both this and another are finite numbers
+
+ // make the denominators the same, then compare numerators
+ long thisNumerator = ((long)mNumerator) * another.mDenominator; // long to avoid overflow
+ long otherNumerator = ((long)another.mNumerator) * mDenominator; // long to avoid overflow
+
+ // avoid underflow from subtraction by doing comparisons
+ if (thisNumerator < otherNumerator) {
+ return -1;
+ } else if (thisNumerator > otherNumerator) {
+ return 1;
+ } else {
+ // This should be covered by #equals, but have this code path just in case
+ return 0;
+ }
+ }
+
+ /*
+ * Serializable implementation.
+ *
+ * The following methods are omitted:
+ * >> writeObject - the default is sufficient (field by field serialization)
+ * >> readObjectNoData - the default is sufficient (0s for both fields is a NaN)
+ */
+
+ /**
+ * writeObject with default serialized form - guards against
+ * deserializing non-reduced forms of the rational.
+ *
+ * @throws InvalidObjectException if the invariants were violated
+ */
+ private void readObject(java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+
+ /*
+ * Guard against trying to deserialize illegal values (in this case, ones
+ * that don't have a standard reduced form).
+ *
+ * - Non-finite values must be one of [0, 1], [0, 0], [0, 1], [0, -1]
+ * - Finite values must always have their greatest common divisor as 1
+ */
+
+ if (mNumerator == 0) { // either zero or NaN
+ if (mDenominator == 1 || mDenominator == 0) {
+ return;
+ }
+ throw new InvalidObjectException(
+ "Rational must be deserialized from a reduced form for zero values");
+ } else if (mDenominator == 0) { // either positive or negative infinity
+ if (mNumerator == 1 || mNumerator == -1) {
+ return;
+ }
+ throw new InvalidObjectException(
+ "Rational must be deserialized from a reduced form for infinity values");
+ } else { // finite value
+ if (gcd(mNumerator, mDenominator) > 1) {
+ throw new InvalidObjectException(
+ "Rational must be deserialized from a reduced form for finite values");
+ }
+ }
+ }
}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 424d860..5056097 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -75,22 +75,10 @@
// Constructors
///////////////////////////////////////////////////////////////////////////
- /**
- * Creates a canvas to render directly on screen.
- */
- GLES20Canvas(boolean translucent) {
- this(false, translucent);
- }
-
- protected GLES20Canvas(boolean record, boolean translucent) {
- mOpaque = !translucent;
-
- if (record) {
- mRenderer = nCreateDisplayListRenderer();
- } else {
- mRenderer = nCreateRenderer();
- }
-
+ // TODO: Merge with GLES20RecordingCanvas
+ protected GLES20Canvas() {
+ mOpaque = false;
+ mRenderer = nCreateDisplayListRenderer();
setupFinalizer();
}
@@ -102,7 +90,6 @@
}
}
- private static native long nCreateRenderer();
private static native long nCreateDisplayListRenderer();
private static native void nResetDisplayListRenderer(long renderer);
private static native void nDestroyRenderer(long renderer);
@@ -131,36 +118,6 @@
private static native void nSetProperty(String name, String value);
///////////////////////////////////////////////////////////////////////////
- // Hardware layers
- ///////////////////////////////////////////////////////////////////////////
-
- @Override
- void pushLayerUpdate(HardwareLayer layer) {
- nPushLayerUpdate(mRenderer, layer.getLayer());
- }
-
- @Override
- void cancelLayerUpdate(HardwareLayer layer) {
- nCancelLayerUpdate(mRenderer, layer.getLayer());
- }
-
- @Override
- void flushLayerUpdates() {
- nFlushLayerUpdates(mRenderer);
- }
-
- @Override
- void clearLayerUpdates() {
- nClearLayerUpdates(mRenderer);
- }
-
- static native boolean nCopyLayer(long layerId, long bitmap);
- private static native void nClearLayerUpdates(long renderer);
- private static native void nFlushLayerUpdates(long renderer);
- private static native void nPushLayerUpdate(long renderer, long layer);
- private static native void nCancelLayerUpdate(long renderer, long layer);
-
- ///////////////////////////////////////////////////////////////////////////
// Canvas management
///////////////////////////////////////////////////////////////////////////
@@ -234,20 +191,6 @@
private static native void nFinish(long renderer);
- /**
- * Returns the size of the stencil buffer required by the underlying
- * implementation.
- *
- * @return The minimum number of bits the stencil buffer must. Always >= 0.
- *
- * @hide
- */
- public static int getStencilSize() {
- return nGetStencilSize();
- }
-
- private static native int nGetStencilSize();
-
///////////////////////////////////////////////////////////////////////////
// Functor
///////////////////////////////////////////////////////////////////////////
@@ -284,49 +227,6 @@
*/
static final int FLUSH_CACHES_FULL = 2;
- /**
- * Flush caches to reclaim as much memory as possible. The amount of memory
- * to reclaim is indicate by the level parameter.
- *
- * The level can be one of {@link #FLUSH_CACHES_MODERATE} or
- * {@link #FLUSH_CACHES_FULL}.
- *
- * @param level Hint about the amount of memory to reclaim
- */
- static void flushCaches(int level) {
- nFlushCaches(level);
- }
-
- private static native void nFlushCaches(int level);
-
- /**
- * Release all resources associated with the underlying caches. This should
- * only be called after a full flushCaches().
- *
- * @hide
- */
- static void terminateCaches() {
- nTerminateCaches();
- }
-
- private static native void nTerminateCaches();
-
- static boolean initCaches() {
- return nInitCaches();
- }
-
- private static native boolean nInitCaches();
-
- ///////////////////////////////////////////////////////////////////////////
- // Atlas
- ///////////////////////////////////////////////////////////////////////////
-
- static void initAtlas(GraphicBuffer buffer, long[] map) {
- nInitAtlas(buffer, map, map.length);
- }
-
- private static native void nInitAtlas(GraphicBuffer buffer, long[] map, int count);
-
///////////////////////////////////////////////////////////////////////////
// Display list
///////////////////////////////////////////////////////////////////////////
@@ -899,12 +799,6 @@
private static native void nDrawPath(long renderer, long path, long paint);
private static native void nDrawRects(long renderer, long region, long paint);
- void drawRects(float[] rects, int count, Paint paint) {
- nDrawRects(mRenderer, rects, count, paint.mNativePaint);
- }
-
- private static native void nDrawRects(long renderer, float[] rects, int count, long paint);
-
@Override
public void drawPicture(Picture picture) {
if (picture.createdFromStream) {
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index a94ec3a..b2961e5 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -36,7 +36,7 @@
RenderNode mNode;
private GLES20RecordingCanvas() {
- super(true, true);
+ super();
}
static GLES20RecordingCanvas obtain(@NonNull RenderNode node) {
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
deleted file mode 100644
index f1163e2..0000000
--- a/core/java/android/view/GLRenderer.java
+++ /dev/null
@@ -1,1521 +0,0 @@
-/*
- * Copyright (C) 2013 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.view;
-
-import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_BAD_NATIVE_WINDOW;
-import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
-import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
-import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_DRAW;
-import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_HEIGHT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
-import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
-import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
-import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
-import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
-import static javax.microedition.khronos.egl.EGL10.EGL_SUCCESS;
-import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
-import static javax.microedition.khronos.egl.EGL10.EGL_WIDTH;
-import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;
-
-import android.content.ComponentCallbacks2;
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.opengl.EGL14;
-import android.opengl.GLUtils;
-import android.opengl.ManagedEGLContext;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Surface.OutOfResourcesException;
-
-import com.google.android.gles_jni.EGLImpl;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.locks.ReentrantLock;
-
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGL11;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.egl.EGLSurface;
-import javax.microedition.khronos.opengles.GL;
-
-/**
- * Hardware renderer using OpenGL
- *
- * @hide
- */
-public class GLRenderer extends HardwareRenderer {
- static final int SURFACE_STATE_ERROR = 0;
- static final int SURFACE_STATE_SUCCESS = 1;
- static final int SURFACE_STATE_UPDATED = 2;
-
- static final int FUNCTOR_PROCESS_DELAY = 4;
-
- /**
- * Number of frames to profile.
- */
- private static final int PROFILE_MAX_FRAMES = 128;
-
- /**
- * Number of floats per profiled frame.
- */
- private static final int PROFILE_FRAME_DATA_COUNT = 3;
-
- private static final int PROFILE_DRAW_MARGIN = 0;
- private static final int PROFILE_DRAW_WIDTH = 3;
- private static final int[] PROFILE_DRAW_COLORS = { 0xcf3e66cc, 0xcfdc3912, 0xcfe69800 };
- private static final int PROFILE_DRAW_CURRENT_FRAME_COLOR = 0xcf5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_COLOR = 0xff5faa4d;
- private static final int PROFILE_DRAW_THRESHOLD_STROKE_WIDTH = 2;
- private static final int PROFILE_DRAW_DP_PER_MS = 7;
-
- private static final String[] VISUALIZERS = {
- PROFILE_PROPERTY_VISUALIZE_BARS,
- };
-
- private static final String[] OVERDRAW = {
- OVERDRAW_PROPERTY_SHOW,
- };
- private static final int GL_VERSION = 2;
-
- static EGL10 sEgl;
- static EGLDisplay sEglDisplay;
- static EGLConfig sEglConfig;
- static final Object[] sEglLock = new Object[0];
- int mWidth = -1, mHeight = -1;
-
- static final ThreadLocal<ManagedEGLContext> sEglContextStorage
- = new ThreadLocal<ManagedEGLContext>();
-
- EGLContext mEglContext;
- Thread mEglThread;
-
- EGLSurface mEglSurface;
-
- GL mGl;
- HardwareCanvas mCanvas;
-
- String mName;
-
- long mFrameCount;
- Paint mDebugPaint;
-
- static boolean sDirtyRegions;
- static final boolean sDirtyRegionsRequested;
- static {
- String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true");
- //noinspection PointlessBooleanExpression,ConstantConditions
- sDirtyRegions = "true".equalsIgnoreCase(dirtyProperty);
- sDirtyRegionsRequested = sDirtyRegions;
- }
-
- boolean mDirtyRegionsEnabled;
- boolean mUpdateDirtyRegions;
-
- boolean mProfileEnabled;
- int mProfileVisualizerType = -1;
- float[] mProfileData;
- ReentrantLock mProfileLock;
- int mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
-
- GraphDataProvider mDebugDataProvider;
- float[][] mProfileShapes;
- Paint mProfilePaint;
-
- boolean mDebugDirtyRegions;
- int mDebugOverdraw = -1;
-
- final boolean mTranslucent;
-
- private boolean mDestroyed;
-
- private final Rect mRedrawClip = new Rect();
-
- private final int[] mSurfaceSize = new int[2];
-
- private long mDrawDelta = Long.MAX_VALUE;
-
- private GLES20Canvas mGlCanvas;
-
- private DisplayMetrics mDisplayMetrics;
-
- private static EGLSurface sPbuffer;
- private static final Object[] sPbufferLock = new Object[0];
-
- private List<HardwareLayer> mLayerUpdates = new ArrayList<HardwareLayer>();
-
- private static class GLRendererEglContext extends ManagedEGLContext {
- final Handler mHandler = new Handler();
-
- public GLRendererEglContext(EGLContext context) {
- super(context);
- }
-
- @Override
- public void onTerminate(final EGLContext eglContext) {
- // Make sure we do this on the correct thread.
- if (mHandler.getLooper() != Looper.myLooper()) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- onTerminate(eglContext);
- }
- });
- return;
- }
-
- synchronized (sEglLock) {
- if (sEgl == null) return;
-
- if (EGLImpl.getInitCount(sEglDisplay) == 1) {
- usePbufferSurface(eglContext);
- GLES20Canvas.terminateCaches();
-
- sEgl.eglDestroyContext(sEglDisplay, eglContext);
- sEglContextStorage.set(null);
- sEglContextStorage.remove();
-
- sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
- EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
- sEgl.eglReleaseThread();
- sEgl.eglTerminate(sEglDisplay);
-
- sEgl = null;
- sEglDisplay = null;
- sEglConfig = null;
- sPbuffer = null;
- }
- }
- }
- }
-
- HardwareCanvas createCanvas() {
- return mGlCanvas = new GLES20Canvas(mTranslucent);
- }
-
- ManagedEGLContext createManagedContext(EGLContext eglContext) {
- return new GLRendererEglContext(mEglContext);
- }
-
- int[] getConfig(boolean dirtyRegions) {
- //noinspection PointlessBooleanExpression,ConstantConditions
- final int stencilSize = GLES20Canvas.getStencilSize();
- final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
-
- return new int[] {
- EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_DEPTH_SIZE, 0,
- EGL_CONFIG_CAVEAT, EGL_NONE,
- EGL_STENCIL_SIZE, stencilSize,
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
- EGL_NONE
- };
- }
-
- void initCaches() {
- if (GLES20Canvas.initCaches()) {
- // Caches were (re)initialized, rebind atlas
- initAtlas();
- }
- }
-
- void initAtlas() {
- IBinder binder = ServiceManager.getService("assetatlas");
- if (binder == null) return;
-
- IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder);
- try {
- if (atlas.isCompatible(android.os.Process.myPpid())) {
- GraphicBuffer buffer = atlas.getBuffer();
- if (buffer != null) {
- long[] map = atlas.getMap();
- if (map != null) {
- GLES20Canvas.initAtlas(buffer, map);
- }
- // If IAssetAtlas is not the same class as the IBinder
- // we are using a remote service and we can safely
- // destroy the graphic buffer
- if (atlas.getClass() != binder.getClass()) {
- buffer.destroy();
- }
- }
- }
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Could not acquire atlas", e);
- }
- }
-
- boolean canDraw() {
- return mGl != null && mCanvas != null && mGlCanvas != null;
- }
-
- int onPreDraw(Rect dirty) {
- return mGlCanvas.onPreDraw(dirty);
- }
-
- void onPostDraw() {
- mGlCanvas.onPostDraw();
- }
-
- void drawProfileData(View.AttachInfo attachInfo) {
- if (mDebugDataProvider != null) {
- final GraphDataProvider provider = mDebugDataProvider;
- initProfileDrawData(attachInfo, provider);
-
- final int height = provider.getVerticalUnitSize();
- final int margin = provider.getHorizontaUnitMargin();
- final int width = provider.getHorizontalUnitSize();
-
- int x = 0;
- int count = 0;
- int current = 0;
-
- final float[] data = provider.getData();
- final int elementCount = provider.getElementCount();
- final int graphType = provider.getGraphType();
-
- int totalCount = provider.getFrameCount() * elementCount;
- if (graphType == GraphDataProvider.GRAPH_TYPE_LINES) {
- totalCount -= elementCount;
- }
-
- for (int i = 0; i < totalCount; i += elementCount) {
- if (data[i] < 0.0f) break;
-
- int index = count * 4;
- if (i == provider.getCurrentFrame() * elementCount) current = index;
-
- x += margin;
- int x2 = x + width;
-
- int y2 = mHeight;
- int y1 = (int) (y2 - data[i] * height);
-
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS: {
- for (int j = 0; j < elementCount; j++) {
- //noinspection MismatchedReadAndWriteOfArray
- final float[] r = mProfileShapes[j];
- r[index] = x;
- r[index + 1] = y1;
- r[index + 2] = x2;
- r[index + 3] = y2;
-
- y2 = y1;
- if (j < elementCount - 1) {
- y1 = (int) (y2 - data[i + j + 1] * height);
- }
- }
- } break;
- case GraphDataProvider.GRAPH_TYPE_LINES: {
- for (int j = 0; j < elementCount; j++) {
- //noinspection MismatchedReadAndWriteOfArray
- final float[] r = mProfileShapes[j];
- r[index] = (x + x2) * 0.5f;
- r[index + 1] = index == 0 ? y1 : r[index - 1];
- r[index + 2] = r[index] + width;
- r[index + 3] = y1;
-
- y2 = y1;
- if (j < elementCount - 1) {
- y1 = (int) (y2 - data[i + j + 1] * height);
- }
- }
- } break;
- }
-
-
- x += width;
- count++;
- }
-
- x += margin;
-
- drawGraph(graphType, count);
- drawCurrentFrame(graphType, current);
- drawThreshold(x, height);
- }
- }
-
- private void drawGraph(int graphType, int count) {
- for (int i = 0; i < mProfileShapes.length; i++) {
- mDebugDataProvider.setupGraphPaint(mProfilePaint, i);
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS:
- mGlCanvas.drawRects(mProfileShapes[i], count * 4, mProfilePaint);
- break;
- case GraphDataProvider.GRAPH_TYPE_LINES:
- mGlCanvas.drawLines(mProfileShapes[i], 0, count * 4, mProfilePaint);
- break;
- }
- }
- }
-
- private void drawCurrentFrame(int graphType, int index) {
- if (index >= 0) {
- mDebugDataProvider.setupCurrentFramePaint(mProfilePaint);
- switch (graphType) {
- case GraphDataProvider.GRAPH_TYPE_BARS:
- mGlCanvas.drawRect(mProfileShapes[2][index], mProfileShapes[2][index + 1],
- mProfileShapes[2][index + 2], mProfileShapes[0][index + 3],
- mProfilePaint);
- break;
- case GraphDataProvider.GRAPH_TYPE_LINES:
- mGlCanvas.drawLine(mProfileShapes[2][index], mProfileShapes[2][index + 1],
- mProfileShapes[2][index], mHeight, mProfilePaint);
- break;
- }
- }
- }
-
- private void drawThreshold(int x, int height) {
- float threshold = mDebugDataProvider.getThreshold();
- if (threshold > 0.0f) {
- mDebugDataProvider.setupThresholdPaint(mProfilePaint);
- int y = (int) (mHeight - threshold * height);
- mGlCanvas.drawLine(0.0f, y, x, y, mProfilePaint);
- }
- }
-
- private void initProfileDrawData(View.AttachInfo attachInfo, GraphDataProvider provider) {
- if (mProfileShapes == null) {
- final int elementCount = provider.getElementCount();
- final int frameCount = provider.getFrameCount();
-
- mProfileShapes = new float[elementCount][];
- for (int i = 0; i < elementCount; i++) {
- mProfileShapes[i] = new float[frameCount * 4];
- }
-
- mProfilePaint = new Paint();
- }
-
- mProfilePaint.reset();
- if (provider.getGraphType() == GraphDataProvider.GRAPH_TYPE_LINES) {
- mProfilePaint.setAntiAlias(true);
- }
-
- if (mDisplayMetrics == null) {
- mDisplayMetrics = new DisplayMetrics();
- }
-
- attachInfo.mDisplay.getMetrics(mDisplayMetrics);
- provider.prepare(mDisplayMetrics);
- }
-
- @Override
- void destroy(boolean full) {
- try {
- if (full && mCanvas != null) {
- mCanvas = null;
- }
-
- if (!isEnabled() || mDestroyed) {
- setEnabled(false);
- return;
- }
-
- destroySurface();
- setEnabled(false);
-
- mDestroyed = true;
- mGl = null;
- } finally {
- if (full && mGlCanvas != null) {
- mGlCanvas = null;
- }
- }
- }
-
- @Override
- void pushLayerUpdate(HardwareLayer layer) {
- mLayerUpdates.add(layer);
- }
-
- @Override
- void flushLayerUpdates() {
- if (validate()) {
- flushLayerChanges();
- mGlCanvas.flushLayerUpdates();
- }
- }
-
- @Override
- HardwareLayer createTextureLayer() {
- validate();
- return HardwareLayer.createTextureLayer(this);
- }
-
- @Override
- public HardwareLayer createDisplayListLayer(int width, int height) {
- validate();
- return HardwareLayer.createDisplayListLayer(this, width, height);
- }
-
- boolean hasContext() {
- return sEgl != null && mEglContext != null
- && mEglContext.equals(sEgl.eglGetCurrentContext());
- }
-
- @Override
- void onLayerDestroyed(HardwareLayer layer) {
- if (mGlCanvas != null) {
- mGlCanvas.cancelLayerUpdate(layer);
- }
- mLayerUpdates.remove(layer);
- }
-
- @Override
- public SurfaceTexture createSurfaceTexture(HardwareLayer layer) {
- return layer.createSurfaceTexture();
- }
-
- @Override
- boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap) {
- if (!validate()) {
- throw new IllegalStateException("Could not acquire hardware rendering context");
- }
- layer.flushChanges();
- return GLES20Canvas.nCopyLayer(layer.getLayer(), bitmap.mNativeBitmap);
- }
-
- @Override
- boolean safelyRun(Runnable action) {
- boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
-
- if (needsContext) {
- GLRendererEglContext managedContext =
- (GLRendererEglContext) sEglContextStorage.get();
- if (managedContext == null) return false;
- usePbufferSurface(managedContext.getContext());
- }
-
- try {
- action.run();
- } finally {
- if (needsContext) {
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
- EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- }
-
- return true;
- }
-
- @Override
- void invokeFunctor(long functor, boolean waitForCompletion) {
- boolean needsContext = !isEnabled() || checkRenderContext() == SURFACE_STATE_ERROR;
- boolean hasContext = !needsContext;
-
- if (needsContext) {
- GLRendererEglContext managedContext =
- (GLRendererEglContext) sEglContextStorage.get();
- if (managedContext != null) {
- usePbufferSurface(managedContext.getContext());
- hasContext = true;
- }
- }
-
- try {
- nInvokeFunctor(functor, hasContext);
- } finally {
- if (needsContext) {
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
- EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- }
- }
-
- private static native void nInvokeFunctor(long functor, boolean hasContext);
-
- @Override
- void destroyHardwareResources(final View view) {
- if (view != null) {
- safelyRun(new Runnable() {
- @Override
- public void run() {
- if (mCanvas != null) {
- mCanvas.clearLayerUpdates();
- }
- destroyResources(view);
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
- }
- });
- }
- }
-
- private static void destroyResources(View view) {
- view.destroyHardwareResources();
-
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
-
- int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- destroyResources(group.getChildAt(i));
- }
- }
- }
-
- static void startTrimMemory(int level) {
- if (sEgl == null || sEglConfig == null) return;
-
- GLRendererEglContext managedContext =
- (GLRendererEglContext) sEglContextStorage.get();
- // We do not have OpenGL objects
- if (managedContext == null) {
- return;
- } else {
- usePbufferSurface(managedContext.getContext());
- }
-
- if (level >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_FULL);
- } else if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
- GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_MODERATE);
- }
- }
-
- static void endTrimMemory() {
- if (sEgl != null && sEglDisplay != null) {
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- }
-
- private static void usePbufferSurface(EGLContext eglContext) {
- synchronized (sPbufferLock) {
- // Create a temporary 1x1 pbuffer so we have a context
- // to clear our OpenGL objects
- if (sPbuffer == null) {
- sPbuffer = sEgl.eglCreatePbufferSurface(sEglDisplay, sEglConfig, new int[] {
- EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE
- });
- }
- }
- sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext);
- }
-
- GLRenderer(boolean translucent) {
- mTranslucent = translucent;
-
- loadSystemProperties();
- }
-
- @Override
- void setOpaque(boolean opaque) {
- // Not supported
- }
-
- @Override
- boolean loadSystemProperties() {
- boolean value;
- boolean changed = false;
-
- String profiling = SystemProperties.get(PROFILE_PROPERTY);
- int graphType = search(VISUALIZERS, profiling);
- value = graphType >= 0;
-
- if (graphType != mProfileVisualizerType) {
- changed = true;
- mProfileVisualizerType = graphType;
-
- mProfileShapes = null;
- mProfilePaint = null;
-
- if (value) {
- mDebugDataProvider = new GraphDataProvider(graphType);
- } else {
- mDebugDataProvider = null;
- }
- }
-
- // If on-screen profiling is not enabled, we need to check whether
- // console profiling only is enabled
- if (!value) {
- value = Boolean.parseBoolean(profiling);
- }
-
- if (value != mProfileEnabled) {
- changed = true;
- mProfileEnabled = value;
-
- if (mProfileEnabled) {
- Log.d(LOG_TAG, "Profiling hardware renderer");
-
- int maxProfileFrames = SystemProperties.getInt(PROFILE_MAXFRAMES_PROPERTY,
- PROFILE_MAX_FRAMES);
- mProfileData = new float[maxProfileFrames * PROFILE_FRAME_DATA_COUNT];
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
- }
-
- mProfileLock = new ReentrantLock();
- } else {
- mProfileData = null;
- mProfileLock = null;
- mProfileVisualizerType = -1;
- }
-
- mProfileCurrentFrame = -PROFILE_FRAME_DATA_COUNT;
- }
-
- value = SystemProperties.getBoolean(DEBUG_DIRTY_REGIONS_PROPERTY, false);
- if (value != mDebugDirtyRegions) {
- changed = true;
- mDebugDirtyRegions = value;
-
- if (mDebugDirtyRegions) {
- Log.d(LOG_TAG, "Debugging dirty regions");
- }
- }
-
- String overdraw = SystemProperties.get(HardwareRenderer.DEBUG_OVERDRAW_PROPERTY);
- int debugOverdraw = search(OVERDRAW, overdraw);
- if (debugOverdraw != mDebugOverdraw) {
- changed = true;
- mDebugOverdraw = debugOverdraw;
- }
-
- if (loadProperties()) {
- changed = true;
- }
-
- return changed;
- }
-
- private static int search(String[] values, String value) {
- for (int i = 0; i < values.length; i++) {
- if (values[i].equals(value)) return i;
- }
- return -1;
- }
-
- @Override
- void dumpGfxInfo(PrintWriter pw, FileDescriptor fd) {
- if (mProfileEnabled) {
- pw.printf("\n\tDraw\tProcess\tExecute\n");
-
- mProfileLock.lock();
- try {
- for (int i = 0; i < mProfileData.length; i += PROFILE_FRAME_DATA_COUNT) {
- if (mProfileData[i] < 0) {
- break;
- }
- pw.printf("\t%3.2f\t%3.2f\t%3.2f\n", mProfileData[i], mProfileData[i + 1],
- mProfileData[i + 2]);
- mProfileData[i] = mProfileData[i + 1] = mProfileData[i + 2] = -1;
- }
- mProfileCurrentFrame = mProfileData.length;
- } finally {
- mProfileLock.unlock();
- }
- }
- }
-
- /**
- * Indicates whether this renderer instance can track and update dirty regions.
- */
- boolean hasDirtyRegions() {
- return mDirtyRegionsEnabled;
- }
-
- /**
- * Checks for OpenGL errors. If an error has occured, {@link #destroy(boolean)}
- * is invoked and the requested flag is turned off. The error code is
- * also logged as a warning.
- */
- void checkEglErrors() {
- if (isEnabled()) {
- checkEglErrorsForced();
- }
- }
-
- private void checkEglErrorsForced() {
- int error = sEgl.eglGetError();
- if (error != EGL_SUCCESS) {
- // something bad has happened revert to
- // normal rendering.
- Log.w(LOG_TAG, "EGL error: " + GLUtils.getEGLErrorString(error));
- fallback(error != EGL11.EGL_CONTEXT_LOST);
- }
- }
-
- private void fallback(boolean fallback) {
- destroy(true);
- if (fallback) {
- // we'll try again if it was context lost
- setRequested(false);
- Log.w(LOG_TAG, "Mountain View, we've had a problem here. "
- + "Switching back to software rendering.");
- }
- }
-
- @Override
- boolean initialize(Surface surface) throws OutOfResourcesException {
- if (isRequested() && !isEnabled()) {
- boolean contextCreated = initializeEgl();
- mGl = createEglSurface(surface);
- mDestroyed = false;
-
- if (mGl != null) {
- int err = sEgl.eglGetError();
- if (err != EGL_SUCCESS) {
- destroy(true);
- setRequested(false);
- } else {
- if (mCanvas == null) {
- mCanvas = createCanvas();
- }
- setEnabled(true);
-
- if (contextCreated) {
- initAtlas();
- }
- }
-
- return mCanvas != null;
- }
- }
- return false;
- }
-
- @Override
- void updateSurface(Surface surface) throws OutOfResourcesException {
- if (isRequested() && isEnabled()) {
- createEglSurface(surface);
- }
- }
-
- @Override
- void pauseSurface(Surface surface) {
- // No-op
- }
-
- boolean initializeEgl() {
- synchronized (sEglLock) {
- if (sEgl == null && sEglConfig == null) {
- sEgl = (EGL10) EGLContext.getEGL();
-
- // Get to the default display.
- sEglDisplay = sEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
- if (sEglDisplay == EGL_NO_DISPLAY) {
- throw new RuntimeException("eglGetDisplay failed "
- + GLUtils.getEGLErrorString(sEgl.eglGetError()));
- }
-
- // We can now initialize EGL for that display
- int[] version = new int[2];
- if (!sEgl.eglInitialize(sEglDisplay, version)) {
- throw new RuntimeException("eglInitialize failed " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- }
-
- checkEglErrorsForced();
-
- sEglConfig = loadEglConfig();
- }
- }
-
- ManagedEGLContext managedContext = sEglContextStorage.get();
- mEglContext = managedContext != null ? managedContext.getContext() : null;
- mEglThread = Thread.currentThread();
-
- if (mEglContext == null) {
- mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
- sEglContextStorage.set(createManagedContext(mEglContext));
- return true;
- }
-
- return false;
- }
-
- private EGLConfig loadEglConfig() {
- EGLConfig eglConfig = chooseEglConfig();
- if (eglConfig == null) {
- // We tried to use EGL_SWAP_BEHAVIOR_PRESERVED_BIT, try again without
- if (sDirtyRegions) {
- sDirtyRegions = false;
- eglConfig = chooseEglConfig();
- if (eglConfig == null) {
- throw new RuntimeException("eglConfig not initialized");
- }
- } else {
- throw new RuntimeException("eglConfig not initialized");
- }
- }
- return eglConfig;
- }
-
- private EGLConfig chooseEglConfig() {
- EGLConfig[] configs = new EGLConfig[1];
- int[] configsCount = new int[1];
- int[] configSpec = getConfig(sDirtyRegions);
-
- // Debug
- final String debug = SystemProperties.get(PRINT_CONFIG_PROPERTY, "");
- if ("all".equalsIgnoreCase(debug)) {
- sEgl.eglChooseConfig(sEglDisplay, configSpec, null, 0, configsCount);
-
- EGLConfig[] debugConfigs = new EGLConfig[configsCount[0]];
- sEgl.eglChooseConfig(sEglDisplay, configSpec, debugConfigs,
- configsCount[0], configsCount);
-
- for (EGLConfig config : debugConfigs) {
- printConfig(config);
- }
- }
-
- if (!sEgl.eglChooseConfig(sEglDisplay, configSpec, configs, 1, configsCount)) {
- throw new IllegalArgumentException("eglChooseConfig failed " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- } else if (configsCount[0] > 0) {
- if ("choice".equalsIgnoreCase(debug)) {
- printConfig(configs[0]);
- }
- return configs[0];
- }
-
- return null;
- }
-
- private static void printConfig(EGLConfig config) {
- int[] value = new int[1];
-
- Log.d(LOG_TAG, "EGL configuration " + config + ":");
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_RED_SIZE, value);
- Log.d(LOG_TAG, " RED_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_GREEN_SIZE, value);
- Log.d(LOG_TAG, " GREEN_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_BLUE_SIZE, value);
- Log.d(LOG_TAG, " BLUE_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_ALPHA_SIZE, value);
- Log.d(LOG_TAG, " ALPHA_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_DEPTH_SIZE, value);
- Log.d(LOG_TAG, " DEPTH_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_STENCIL_SIZE, value);
- Log.d(LOG_TAG, " STENCIL_SIZE = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLE_BUFFERS, value);
- Log.d(LOG_TAG, " SAMPLE_BUFFERS = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SAMPLES, value);
- Log.d(LOG_TAG, " SAMPLES = " + value[0]);
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_SURFACE_TYPE, value);
- Log.d(LOG_TAG, " SURFACE_TYPE = 0x" + Integer.toHexString(value[0]));
-
- sEgl.eglGetConfigAttrib(sEglDisplay, config, EGL_CONFIG_CAVEAT, value);
- Log.d(LOG_TAG, " CONFIG_CAVEAT = 0x" + Integer.toHexString(value[0]));
- }
-
- GL createEglSurface(Surface surface) throws OutOfResourcesException {
- // Check preconditions.
- if (sEgl == null) {
- throw new RuntimeException("egl not initialized");
- }
- if (sEglDisplay == null) {
- throw new RuntimeException("eglDisplay not initialized");
- }
- if (sEglConfig == null) {
- throw new RuntimeException("eglConfig not initialized");
- }
- if (Thread.currentThread() != mEglThread) {
- throw new IllegalStateException("HardwareRenderer cannot be used "
- + "from multiple threads");
- }
-
- // In case we need to destroy an existing surface
- destroySurface();
-
- // Create an EGL surface we can render into.
- if (!createSurface(surface)) {
- return null;
- }
-
- initCaches();
-
- return mEglContext.getGL();
- }
-
- private void enableDirtyRegions() {
- // If mDirtyRegions is set, this means we have an EGL configuration
- // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set
- if (sDirtyRegions) {
- if (!(mDirtyRegionsEnabled = preserveBackBuffer())) {
- Log.w(LOG_TAG, "Backbuffer cannot be preserved");
- }
- } else if (sDirtyRegionsRequested) {
- // If mDirtyRegions is not set, our EGL configuration does not
- // have EGL_SWAP_BEHAVIOR_PRESERVED_BIT; however, the default
- // swap behavior might be EGL_BUFFER_PRESERVED, which means we
- // want to set mDirtyRegions. We try to do this only if dirty
- // regions were initially requested as part of the device
- // configuration (see RENDER_DIRTY_REGIONS)
- mDirtyRegionsEnabled = isBackBufferPreserved();
- }
- }
-
- EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
- final int[] attribs = { EGL14.EGL_CONTEXT_CLIENT_VERSION, GL_VERSION, EGL_NONE };
-
- EGLContext context = egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT,
- attribs);
- if (context == null || context == EGL_NO_CONTEXT) {
- //noinspection ConstantConditions
- throw new IllegalStateException(
- "Could not create an EGL context. eglCreateContext failed with error: " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- }
-
- return context;
- }
-
- void destroySurface() {
- if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
- if (mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW))) {
- sEgl.eglMakeCurrent(sEglDisplay,
- EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- }
- sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
- mEglSurface = null;
- }
- }
-
- @Override
- void invalidate(Surface surface) {
- // Cancels any existing buffer to ensure we'll get a buffer
- // of the right size before we call eglSwapBuffers
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
- if (mEglSurface != null && mEglSurface != EGL_NO_SURFACE) {
- sEgl.eglDestroySurface(sEglDisplay, mEglSurface);
- mEglSurface = null;
- setEnabled(false);
- }
-
- if (surface.isValid()) {
- if (!createSurface(surface)) {
- return;
- }
-
- mUpdateDirtyRegions = true;
-
- if (mCanvas != null) {
- setEnabled(true);
- }
- }
- }
-
- private boolean createSurface(Surface surface) {
- mEglSurface = sEgl.eglCreateWindowSurface(sEglDisplay, sEglConfig, surface, null);
-
- if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
- int error = sEgl.eglGetError();
- if (error == EGL_BAD_NATIVE_WINDOW) {
- Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
- return false;
- }
- throw new RuntimeException("createWindowSurface failed "
- + GLUtils.getEGLErrorString(error));
- }
-
- if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- throw new IllegalStateException("eglMakeCurrent failed " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- }
-
- enableDirtyRegions();
-
- return true;
- }
-
- boolean validate() {
- return checkRenderContext() != SURFACE_STATE_ERROR;
- }
-
- @Override
- void setup(int width, int height, float lightX, float lightY, float lightZ, float lightRadius) {
- if (validate()) {
- mCanvas.setViewport(width, height);
- mCanvas.initializeLight(lightX, lightY, lightZ, lightRadius);
- mWidth = width;
- mHeight = height;
- }
- }
-
- @Override
- int getWidth() {
- return mWidth;
- }
-
- @Override
- int getHeight() {
- return mHeight;
- }
-
- @Override
- void setName(String name) {
- mName = name;
- }
-
- @Override
- void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
- Rect dirty) {
- if (canDraw()) {
- if (!hasDirtyRegions()) {
- dirty = null;
- }
- attachInfo.mIgnoreDirtyState = true;
- attachInfo.mDrawingTime = SystemClock.uptimeMillis();
-
- view.mPrivateFlags |= View.PFLAG_DRAWN;
-
- // We are already on the correct thread
- final int surfaceState = checkRenderContextUnsafe();
- if (surfaceState != SURFACE_STATE_ERROR) {
- HardwareCanvas canvas = mCanvas;
-
- if (mProfileEnabled) {
- mProfileLock.lock();
- }
-
- dirty = beginFrame(canvas, dirty, surfaceState);
-
- RenderNode displayList = buildDisplayList(view, canvas);
-
- flushLayerChanges();
-
- // buildDisplayList() calls into user code which can cause
- // an eglMakeCurrent to happen with a different surface/context.
- // We must therefore check again here.
- if (checkRenderContextUnsafe() == SURFACE_STATE_ERROR) {
- return;
- }
-
- int saveCount = 0;
- int status = RenderNode.STATUS_DONE;
-
- long start = getSystemTime();
- try {
- status = prepareFrame(dirty);
-
- saveCount = canvas.save();
- callbacks.onHardwarePreDraw(canvas);
-
- if (displayList != null) {
- status |= drawDisplayList(canvas, displayList, status);
- } else {
- // Shouldn't reach here
- view.draw(canvas);
- }
- } catch (Exception e) {
- Log.e(LOG_TAG, "An error has occurred while drawing:", e);
- } finally {
- callbacks.onHardwarePostDraw(canvas);
- canvas.restoreToCount(saveCount);
- view.mRecreateDisplayList = false;
-
- mDrawDelta = getSystemTime() - start;
-
- if (mDrawDelta > 0) {
- mFrameCount++;
-
- debugDirtyRegions(dirty, canvas);
- drawProfileData(attachInfo);
- }
- }
-
- onPostDraw();
-
- swapBuffers(status);
-
- if (mProfileEnabled) {
- mProfileLock.unlock();
- }
-
- attachInfo.mIgnoreDirtyState = false;
- }
- }
- }
-
- private void flushLayerChanges() {
- // Loop through and apply any pending layer changes
- for (int i = 0; i < mLayerUpdates.size(); i++) {
- HardwareLayer layer = mLayerUpdates.get(i);
- layer.flushChanges();
- if (!layer.isValid()) {
- // The layer was removed from mAttachedLayers, rewind i by 1
- // Note that this shouldn't actually happen as View.getHardwareLayer()
- // is already flushing for error checking reasons
- i--;
- } else if (layer.hasDisplayList()) {
- mCanvas.pushLayerUpdate(layer);
- }
- }
- mLayerUpdates.clear();
- }
-
- @Override
- void fence() {
- // Everything is immediate, so this is a no-op
- }
-
- private RenderNode buildDisplayList(View view, HardwareCanvas canvas) {
- view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
- == View.PFLAG_INVALIDATED;
- view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
-
- long buildDisplayListStartTime = startBuildDisplayListProfiling();
- canvas.clearLayerUpdates();
-
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
- RenderNode renderNode = view.getDisplayList();
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-
- endBuildDisplayListProfiling(buildDisplayListStartTime);
-
- return renderNode;
- }
-
- private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
- // We had to change the current surface and/or context, redraw everything
- if (surfaceState == SURFACE_STATE_UPDATED) {
- dirty = null;
- beginFrame(null);
- } else {
- int[] size = mSurfaceSize;
- beginFrame(size);
-
- if (size[1] != mHeight || size[0] != mWidth) {
- mWidth = size[0];
- mHeight = size[1];
-
- canvas.setViewport(mWidth, mHeight);
-
- dirty = null;
- }
- }
-
- if (mDebugDataProvider != null) dirty = null;
-
- return dirty;
- }
-
- private long startBuildDisplayListProfiling() {
- if (mProfileEnabled) {
- mProfileCurrentFrame += PROFILE_FRAME_DATA_COUNT;
- if (mProfileCurrentFrame >= mProfileData.length) {
- mProfileCurrentFrame = 0;
- }
-
- return System.nanoTime();
- }
- return 0;
- }
-
- private void endBuildDisplayListProfiling(long getDisplayListStartTime) {
- if (mProfileEnabled) {
- long now = System.nanoTime();
- float total = (now - getDisplayListStartTime) * 0.000001f;
- //noinspection PointlessArithmeticExpression
- mProfileData[mProfileCurrentFrame] = total;
- }
- }
-
- private int prepareFrame(Rect dirty) {
- int status;
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
- try {
- status = onPreDraw(dirty);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
- return status;
- }
-
- private int drawDisplayList(HardwareCanvas canvas, RenderNode displayList,
- int status) {
-
- long drawDisplayListStartTime = 0;
- if (mProfileEnabled) {
- drawDisplayListStartTime = System.nanoTime();
- }
-
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "drawDisplayList");
- nPrepareTree(displayList.getNativeDisplayList());
- try {
- status |= canvas.drawDisplayList(displayList, mRedrawClip,
- RenderNode.FLAG_CLIP_CHILDREN);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
-
- if (mProfileEnabled) {
- long now = System.nanoTime();
- float total = (now - drawDisplayListStartTime) * 0.000001f;
- mProfileData[mProfileCurrentFrame + 1] = total;
- }
-
- return status;
- }
-
- private void swapBuffers(int status) {
- if ((status & RenderNode.STATUS_DREW) == RenderNode.STATUS_DREW) {
- long eglSwapBuffersStartTime = 0;
- if (mProfileEnabled) {
- eglSwapBuffersStartTime = System.nanoTime();
- }
-
- sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
-
- if (mProfileEnabled) {
- long now = System.nanoTime();
- float total = (now - eglSwapBuffersStartTime) * 0.000001f;
- mProfileData[mProfileCurrentFrame + 2] = total;
- }
-
- checkEglErrors();
- }
- }
-
- private void debugDirtyRegions(Rect dirty, HardwareCanvas canvas) {
- if (mDebugDirtyRegions) {
- if (mDebugPaint == null) {
- mDebugPaint = new Paint();
- mDebugPaint.setColor(0x7fff0000);
- }
-
- if (dirty != null && (mFrameCount & 1) == 0) {
- canvas.drawRect(dirty, mDebugPaint);
- }
- }
- }
-
- /**
- * Ensures the current EGL context and surface are the ones we expect.
- * This method throws an IllegalStateException if invoked from a thread
- * that did not initialize EGL.
- *
- * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
- * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
- * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
- *
- * @see #checkRenderContextUnsafe()
- */
- int checkRenderContext() {
- if (mEglThread != Thread.currentThread()) {
- throw new IllegalStateException("Hardware acceleration can only be used with a " +
- "single UI thread.\nOriginal thread: " + mEglThread + "\n" +
- "Current thread: " + Thread.currentThread());
- }
-
- return checkRenderContextUnsafe();
- }
-
- /**
- * Ensures the current EGL context and surface are the ones we expect.
- * This method does not check the current thread.
- *
- * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
- * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
- * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
- *
- * @see #checkRenderContext()
- */
- private int checkRenderContextUnsafe() {
- if (!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL_DRAW)) ||
- !mEglContext.equals(sEgl.eglGetCurrentContext())) {
- if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
- Log.e(LOG_TAG, "eglMakeCurrent failed " +
- GLUtils.getEGLErrorString(sEgl.eglGetError()));
- fallback(true);
- return SURFACE_STATE_ERROR;
- } else {
- if (mUpdateDirtyRegions) {
- enableDirtyRegions();
- mUpdateDirtyRegions = false;
- }
- return SURFACE_STATE_UPDATED;
- }
- }
- return SURFACE_STATE_SUCCESS;
- }
-
- private static int dpToPx(int dp, float density) {
- return (int) (dp * density + 0.5f);
- }
-
- static native boolean loadProperties();
-
- static native void setupShadersDiskCache(String cacheFile);
-
- /**
- * Notifies EGL that the frame is about to be rendered.
- * @param size
- */
- static native void beginFrame(int[] size);
-
- /**
- * Returns the current system time according to the renderer.
- * This method is used for debugging only and should not be used
- * as a clock.
- */
- static native long getSystemTime();
-
- /**
- * Preserves the back buffer of the current surface after a buffer swap.
- * Calling this method sets the EGL_SWAP_BEHAVIOR attribute of the current
- * surface to EGL_BUFFER_PRESERVED. Calling this method requires an EGL
- * config that supports EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
- *
- * @return True if the swap behavior was successfully changed,
- * false otherwise.
- */
- static native boolean preserveBackBuffer();
-
- /**
- * Indicates whether the current surface preserves its back buffer
- * after a buffer swap.
- *
- * @return True, if the surface's EGL_SWAP_BEHAVIOR is EGL_BUFFER_PRESERVED,
- * false otherwise
- */
- static native boolean isBackBufferPreserved();
-
- static native void nDestroyLayer(long layerPtr);
-
- private static native void nPrepareTree(long displayListPtr);
-
- class GraphDataProvider {
- /**
- * Draws the graph as bars. Frame elements are stacked on top of
- * each other.
- */
- public static final int GRAPH_TYPE_BARS = 0;
- /**
- * Draws the graph as lines. The number of series drawn corresponds
- * to the number of elements.
- */
- public static final int GRAPH_TYPE_LINES = 1;
-
- private final int mGraphType;
-
- private int mVerticalUnit;
- private int mHorizontalUnit;
- private int mHorizontalMargin;
- private int mThresholdStroke;
-
- public GraphDataProvider(int graphType) {
- mGraphType = graphType;
- }
-
- void prepare(DisplayMetrics metrics) {
- final float density = metrics.density;
-
- mVerticalUnit = dpToPx(PROFILE_DRAW_DP_PER_MS, density);
- mHorizontalUnit = dpToPx(PROFILE_DRAW_WIDTH, density);
- mHorizontalMargin = dpToPx(PROFILE_DRAW_MARGIN, density);
- mThresholdStroke = dpToPx(PROFILE_DRAW_THRESHOLD_STROKE_WIDTH, density);
- }
-
- int getGraphType() {
- return mGraphType;
- }
-
- int getVerticalUnitSize() {
- return mVerticalUnit;
- }
-
- int getHorizontalUnitSize() {
- return mHorizontalUnit;
- }
-
- int getHorizontaUnitMargin() {
- return mHorizontalMargin;
- }
-
- float[] getData() {
- return mProfileData;
- }
-
- float getThreshold() {
- return 16;
- }
-
- int getFrameCount() {
- return mProfileData.length / PROFILE_FRAME_DATA_COUNT;
- }
-
- int getElementCount() {
- return PROFILE_FRAME_DATA_COUNT;
- }
-
- int getCurrentFrame() {
- return mProfileCurrentFrame / PROFILE_FRAME_DATA_COUNT;
- }
-
- void setupGraphPaint(Paint paint, int elementIndex) {
- paint.setColor(PROFILE_DRAW_COLORS[elementIndex]);
- if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
- }
-
- void setupThresholdPaint(Paint paint) {
- paint.setColor(PROFILE_DRAW_THRESHOLD_COLOR);
- paint.setStrokeWidth(mThresholdStroke);
- }
-
- void setupCurrentFramePaint(Paint paint) {
- paint.setColor(PROFILE_DRAW_CURRENT_FRAME_COLOR);
- if (mGraphType == GRAPH_TYPE_LINES) paint.setStrokeWidth(mThresholdStroke);
- }
- }
-}
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 9568760..b8e7d8c 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -110,48 +110,6 @@
return RenderNode.STATUS_DONE;
}
- /**
- * Indicates that the specified layer must be updated as soon as possible.
- *
- * @param layer The layer to update
- *
- * @see #clearLayerUpdates()
- *
- * @hide
- */
- abstract void pushLayerUpdate(HardwareLayer layer);
-
- /**
- * Cancels a queued layer update. If the specified layer was not
- * queued for update, this method has no effect.
- *
- * @param layer The layer whose update to cancel
- *
- * @see #pushLayerUpdate(HardwareLayer)
- * @see #clearLayerUpdates()
- *
- * @hide
- */
- abstract void cancelLayerUpdate(HardwareLayer layer);
-
- /**
- * Immediately executes all enqueued layer updates.
- *
- * @see #pushLayerUpdate(HardwareLayer)
- *
- * @hide
- */
- abstract void flushLayerUpdates();
-
- /**
- * Removes all enqueued layer updates.
- *
- * @see #pushLayerUpdate(HardwareLayer)
- *
- * @hide
- */
- abstract void clearLayerUpdates();
-
public abstract void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
CanvasProperty<Float> radius, CanvasProperty<Paint> paint);
}
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index 652bcd2..266a6fed 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -19,7 +19,6 @@
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Paint;
-import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import com.android.internal.util.VirtualRefBasePtr;
@@ -34,34 +33,18 @@
* @hide
*/
final class HardwareLayer {
- private static final int LAYER_TYPE_TEXTURE = 1;
- private static final int LAYER_TYPE_DISPLAY_LIST = 2;
-
private HardwareRenderer mRenderer;
private VirtualRefBasePtr mFinalizer;
- private RenderNode mDisplayList;
- private final int mLayerType;
- private HardwareLayer(HardwareRenderer renderer, long deferredUpdater, int type) {
+ private HardwareLayer(HardwareRenderer renderer, long deferredUpdater) {
if (renderer == null || deferredUpdater == 0) {
throw new IllegalArgumentException("Either hardware renderer: " + renderer
+ " or deferredUpdater: " + deferredUpdater + " is invalid");
}
mRenderer = renderer;
- mLayerType = type;
mFinalizer = new VirtualRefBasePtr(deferredUpdater);
}
- private void assertType(int type) {
- if (mLayerType != type) {
- throw new IllegalAccessError("Method not appropriate for this layer type! " + mLayerType);
- }
- }
-
- boolean hasDisplayList() {
- return mDisplayList != null;
- }
-
/**
* Update the paint used when drawing this layer.
*
@@ -90,11 +73,6 @@
// Already destroyed
return;
}
-
- if (mDisplayList != null) {
- mDisplayList.destroyDisplayListData();
- mDisplayList = null;
- }
mRenderer.onLayerDestroyed(this);
mRenderer = null;
mFinalizer.release();
@@ -105,21 +83,6 @@
return mFinalizer.get();
}
- public RenderNode startRecording() {
- assertType(LAYER_TYPE_DISPLAY_LIST);
-
- if (mDisplayList == null) {
- mDisplayList = RenderNode.create("HardwareLayer");
- }
- return mDisplayList;
- }
-
- public void endRecording(Rect dirtyRect) {
- nUpdateRenderLayer(mFinalizer.get(), mDisplayList.getNativeDisplayList(),
- dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom);
- mRenderer.pushLayerUpdate(this);
- }
-
/**
* Copies this layer into the specified bitmap.
*
@@ -160,7 +123,6 @@
* Indicates that this layer has lost its texture.
*/
public void detachSurfaceTexture(final SurfaceTexture surface) {
- assertType(LAYER_TYPE_TEXTURE);
mRenderer.safelyRun(new Runnable() {
@Override
public void run() {
@@ -172,36 +134,16 @@
});
}
- /**
- * This exists to minimize impact into the current HardwareLayer paths as
- * some of the specifics of how to handle error cases in the fully
- * deferred model will work
- */
- @Deprecated
- public void flushChanges() {
- if (HardwareRenderer.sUseRenderThread) {
- // Not supported, don't try.
- return;
- }
-
- boolean success = nFlushChanges(mFinalizer.get());
- if (!success) {
- destroy();
- }
- }
-
public long getLayer() {
return nGetLayer(mFinalizer.get());
}
public void setSurfaceTexture(SurfaceTexture surface) {
- assertType(LAYER_TYPE_TEXTURE);
nSetSurfaceTexture(mFinalizer.get(), surface, false);
mRenderer.pushLayerUpdate(this);
}
public void updateSurfaceTexture() {
- assertType(LAYER_TYPE_TEXTURE);
nUpdateSurfaceTexture(mFinalizer.get());
mRenderer.pushLayerUpdate(this);
}
@@ -210,39 +152,15 @@
* This should only be used by HardwareRenderer! Do not call directly
*/
SurfaceTexture createSurfaceTexture() {
- assertType(LAYER_TYPE_TEXTURE);
SurfaceTexture st = new SurfaceTexture(nGetTexName(mFinalizer.get()));
nSetSurfaceTexture(mFinalizer.get(), st, true);
return st;
}
- /**
- * This should only be used by HardwareRenderer! Do not call directly
- */
- static HardwareLayer createTextureLayer(HardwareRenderer renderer) {
- return new HardwareLayer(renderer, nCreateTextureLayer(), LAYER_TYPE_TEXTURE);
- }
-
static HardwareLayer adoptTextureLayer(HardwareRenderer renderer, long layer) {
- return new HardwareLayer(renderer, layer, LAYER_TYPE_TEXTURE);
+ return new HardwareLayer(renderer, layer);
}
- /**
- * This should only be used by HardwareRenderer! Do not call directly
- */
- static HardwareLayer createDisplayListLayer(HardwareRenderer renderer,
- int width, int height) {
- return new HardwareLayer(renderer, nCreateRenderLayer(width, height), LAYER_TYPE_DISPLAY_LIST);
- }
-
- static HardwareLayer adoptDisplayListLayer(HardwareRenderer renderer, long layer) {
- return new HardwareLayer(renderer, layer, LAYER_TYPE_DISPLAY_LIST);
- }
-
- /** This also creates the underlying layer */
- private static native long nCreateTextureLayer();
- private static native long nCreateRenderLayer(int width, int height);
-
private static native void nOnTextureDestroyed(long layerUpdater);
private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque);
@@ -254,8 +172,6 @@
private static native void nUpdateRenderLayer(long layerUpdater, long displayList,
int left, int top, int right, int bottom);
- private static native boolean nFlushChanges(long layerUpdater);
-
private static native long nGetLayer(long layerUpdater);
private static native int nGetTexName(long layerUpdater);
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index d71de9f..3de8144 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -171,9 +171,6 @@
*/
public static boolean sSystemRendererDisabled = false;
- /** @hide */
- public static boolean sUseRenderThread = true;
-
private boolean mEnabled;
private boolean mRequested = true;
@@ -309,7 +306,7 @@
* @hide
*/
public static void setupDiskCache(File cacheDir) {
- GLRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
+ ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
}
/**
@@ -366,8 +363,7 @@
* @param callbacks Callbacks invoked when drawing happens.
* @param dirty The dirty rectangle to update, can be null.
*/
- abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
- Rect dirty);
+ abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks);
/**
* Creates a new hardware layer. A hardware layer built by calling this
@@ -378,16 +374,6 @@
abstract HardwareLayer createTextureLayer();
/**
- * Creates a new hardware layer.
- *
- * @param width The minimum width of the layer
- * @param height The minimum height of the layer
- *
- * @return A hardware layer
- */
- abstract HardwareLayer createDisplayListLayer(int width, int height);
-
- /**
* Creates a new {@link SurfaceTexture} that can be used to render into the
* specified hardware layer.
*
@@ -469,11 +455,7 @@
static HardwareRenderer create(boolean translucent) {
HardwareRenderer renderer = null;
if (GLES20Canvas.isAvailable()) {
- if (sUseRenderThread) {
- renderer = new ThreadedRenderer(translucent);
- } else {
- renderer = new GLRenderer(translucent);
- }
+ renderer = new ThreadedRenderer(translucent);
}
return renderer;
}
@@ -500,7 +482,7 @@
* see {@link android.content.ComponentCallbacks}
*/
static void startTrimMemory(int level) {
- GLRenderer.startTrimMemory(level);
+ ThreadedRenderer.startTrimMemory(level);
}
/**
@@ -508,7 +490,7 @@
* cleanup special resources used by the memory trimming process.
*/
static void endTrimMemory() {
- GLRenderer.endTrimMemory();
+ ThreadedRenderer.endTrimMemory();
}
/**
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index af16185..ae59bbc 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -30,6 +30,7 @@
import android.view.IOnKeyguardExitResult;
import android.view.IRotationWatcher;
import android.view.IWindowSession;
+import android.view.IWindowSessionCallback;
import android.view.KeyEvent;
import android.view.InputEvent;
import android.view.MagnificationSpec;
@@ -56,7 +57,7 @@
boolean stopViewServer(); // Transaction #2
boolean isViewServerRunning(); // Transaction #3
- IWindowSession openSession(in IInputMethodClient client,
+ IWindowSession openSession(in IWindowSessionCallback callback, in IInputMethodClient client,
in IInputContext inputContext);
boolean inputMethodClientHasFocus(IInputMethodClient client);
@@ -130,6 +131,8 @@
void setAnimationScale(int which, float scale);
void setAnimationScales(in float[] scales);
+ float getCurrentAnimatorScale();
+
// For testing
void setInTouchMode(boolean showFocus);
@@ -216,12 +219,6 @@
oneway void statusBarVisibilityChanged(int visibility);
/**
- * Block until the given window has been drawn to the screen.
- * Returns true if really waiting, false if the window does not exist.
- */
- boolean waitForWindowDrawn(IBinder token, in IRemoteCallback callback);
-
- /**
* Device has a software navigation bar (separate from the status bar).
*/
boolean hasNavigationBar();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index c32a2c9..fa5bd88 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -46,7 +46,7 @@
int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets);
void remove(IWindow window);
-
+
/**
* Change the parameters of a window. You supply the
* new parameters, it returns the new frame of the window on screen (the
@@ -109,7 +109,7 @@
* to optimize compositing of this part of the window.
*/
void setTransparentRegion(IWindow window, in Region region);
-
+
/**
* Tell the window manager about the content and visible insets of the
* given window, which can be used to adjust the <var>outContentInsets</var>
@@ -122,20 +122,20 @@
*/
void setInsets(IWindow window, int touchableInsets, in Rect contentInsets,
in Rect visibleInsets, in Region touchableRegion);
-
+
/**
* Return the current display size in which the window is being laid out,
* accounting for screen decorations around it.
*/
void getDisplayFrame(IWindow window, out Rect outDisplayFrame);
-
+
void finishDrawing(IWindow window);
-
+
void setInTouchMode(boolean showFocus);
boolean getInTouchMode();
-
+
boolean performHapticFeedback(IWindow window, int effectId, boolean always);
-
+
/**
* Allocate the drag's thumbnail surface. Also assigns a token that identifies
* the drag to the OS and passes that as the return value. A return value of
@@ -150,11 +150,11 @@
boolean performDrag(IWindow window, IBinder dragToken, float touchX, float touchY,
float thumbCenterX, float thumbCenterY, in ClipData data);
- /**
- * Report the result of a drop action targeted to the given window.
- * consumed is 'true' when the drop was accepted by a valid recipient,
- * 'false' otherwise.
- */
+ /**
+ * Report the result of a drop action targeted to the given window.
+ * consumed is 'true' when the drop was accepted by a valid recipient,
+ * 'false' otherwise.
+ */
void reportDropResult(IWindow window, boolean consumed);
/**
@@ -174,12 +174,12 @@
* how big the increment is from one screen to another.
*/
void setWallpaperPosition(IBinder windowToken, float x, float y, float xstep, float ystep);
-
+
void wallpaperOffsetsComplete(IBinder window);
-
+
Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, in Bundle extras, boolean sync);
-
+
void wallpaperCommandComplete(IBinder window, in Bundle result);
void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
@@ -188,7 +188,7 @@
/**
* Notifies that a rectangle on the screen has been requested.
*/
- void onRectangleOnScreenRequested(IBinder token, in Rect rectangle, boolean immediate);
+ void onRectangleOnScreenRequested(IBinder token, in Rect rectangle);
IWindowId getWindowId(IBinder window);
}
diff --git a/core/java/android/view/IWindowSessionCallback.aidl b/core/java/android/view/IWindowSessionCallback.aidl
new file mode 100644
index 0000000..88931ce
--- /dev/null
+++ b/core/java/android/view/IWindowSessionCallback.aidl
@@ -0,0 +1,27 @@
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+/**
+ * Callback to active sessions of the window manager
+ *
+ * {@hide}
+ */
+oneway interface IWindowSessionCallback
+{
+ void onAnimatorScaleChanged(float scale);
+}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 8a996d2..8b2ec7a 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1716,6 +1716,7 @@
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_SLEEP:
case KeyEvent.KEYCODE_WAKEUP:
+ case KeyEvent.KEYCODE_PAIRING:
return true;
}
return false;
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index e63829e..d86b699 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.graphics.Matrix;
import android.graphics.Outline;
+import android.graphics.Paint;
import java.util.ArrayList;
import java.util.List;
@@ -322,11 +323,13 @@
* handled in the drawLayer operation directly (and more efficiently).
*
* @param caching true if the display list represents a hardware layer, false otherwise.
- *
- * @hide
*/
- public void setCaching(boolean caching) {
- nSetCaching(mNativeRenderNode, caching);
+ public boolean setLayerType(int layerType) {
+ return nSetLayerType(mNativeRenderNode, layerType);
+ }
+
+ public boolean setLayerPaint(Paint paint) {
+ return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.mNativePaint : 0);
}
/**
@@ -335,8 +338,8 @@
*
* @param clipToBounds true if the display list should clip to its bounds
*/
- public void setClipToBounds(boolean clipToBounds) {
- nSetClipToBounds(mNativeRenderNode, clipToBounds);
+ public boolean setClipToBounds(boolean clipToBounds) {
+ return nSetClipToBounds(mNativeRenderNode, clipToBounds);
}
/**
@@ -346,8 +349,8 @@
* @param shouldProject true if the display list should be projected onto a
* containing volume.
*/
- public void setProjectBackwards(boolean shouldProject) {
- nSetProjectBackwards(mNativeRenderNode, shouldProject);
+ public boolean setProjectBackwards(boolean shouldProject) {
+ return nSetProjectBackwards(mNativeRenderNode, shouldProject);
}
/**
@@ -355,8 +358,8 @@
* DisplayList should draw any descendent DisplayLists with
* ProjectBackwards=true directly on top of it. Default value is false.
*/
- public void setProjectionReceiver(boolean shouldRecieve) {
- nSetProjectionReceiver(mNativeRenderNode, shouldRecieve);
+ public boolean setProjectionReceiver(boolean shouldRecieve) {
+ return nSetProjectionReceiver(mNativeRenderNode, shouldRecieve);
}
/**
@@ -365,15 +368,16 @@
*
* Deep copies the data into native to simplify reference ownership.
*/
- public void setOutline(Outline outline) {
+ public boolean setOutline(Outline outline) {
if (outline == null || outline.isEmpty()) {
- nSetOutlineEmpty(mNativeRenderNode);
+ return nSetOutlineEmpty(mNativeRenderNode);
} else if (outline.mRect != null) {
- nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top,
+ return nSetOutlineRoundRect(mNativeRenderNode, outline.mRect.left, outline.mRect.top,
outline.mRect.right, outline.mRect.bottom, outline.mRadius);
} else if (outline.mPath != null) {
- nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath);
+ return nSetOutlineConvexPath(mNativeRenderNode, outline.mPath.mNativePath);
}
+ throw new IllegalArgumentException("Unrecognized outline?");
}
/**
@@ -381,8 +385,8 @@
*
* @param clipToOutline true if clipping to the outline.
*/
- public void setClipToOutline(boolean clipToOutline) {
- nSetClipToOutline(mNativeRenderNode, clipToOutline);
+ public boolean setClipToOutline(boolean clipToOutline) {
+ return nSetClipToOutline(mNativeRenderNode, clipToOutline);
}
public boolean getClipToOutline() {
@@ -392,9 +396,9 @@
/**
* Controls the RenderNode's circular reveal clip.
*/
- public void setRevealClip(boolean shouldClip, boolean inverseClip,
+ public boolean setRevealClip(boolean shouldClip, boolean inverseClip,
float x, float y, float radius) {
- nSetRevealClip(mNativeRenderNode, shouldClip, inverseClip, x, y, radius);
+ return nSetRevealClip(mNativeRenderNode, shouldClip, inverseClip, x, y, radius);
}
/**
@@ -403,8 +407,8 @@
*
* @param matrix A transform matrix to apply to this display list
*/
- public void setStaticMatrix(Matrix matrix) {
- nSetStaticMatrix(mNativeRenderNode, matrix.native_instance);
+ public boolean setStaticMatrix(Matrix matrix) {
+ return nSetStaticMatrix(mNativeRenderNode, matrix.native_instance);
}
/**
@@ -417,8 +421,8 @@
*
* @hide
*/
- public void setAnimationMatrix(Matrix matrix) {
- nSetAnimationMatrix(mNativeRenderNode,
+ public boolean setAnimationMatrix(Matrix matrix) {
+ return nSetAnimationMatrix(mNativeRenderNode,
(matrix != null) ? matrix.native_instance : 0);
}
@@ -430,8 +434,8 @@
* @see View#setAlpha(float)
* @see #getAlpha()
*/
- public void setAlpha(float alpha) {
- nSetAlpha(mNativeRenderNode, alpha);
+ public boolean setAlpha(float alpha) {
+ return nSetAlpha(mNativeRenderNode, alpha);
}
/**
@@ -456,8 +460,8 @@
* @see android.view.View#hasOverlappingRendering()
* @see #hasOverlappingRendering()
*/
- public void setHasOverlappingRendering(boolean hasOverlappingRendering) {
- nSetHasOverlappingRendering(mNativeRenderNode, hasOverlappingRendering);
+ public boolean setHasOverlappingRendering(boolean hasOverlappingRendering) {
+ return nSetHasOverlappingRendering(mNativeRenderNode, hasOverlappingRendering);
}
/**
@@ -472,8 +476,8 @@
return nHasOverlappingRendering(mNativeRenderNode);
}
- public void setElevation(float lift) {
- nSetElevation(mNativeRenderNode, lift);
+ public boolean setElevation(float lift) {
+ return nSetElevation(mNativeRenderNode, lift);
}
public float getElevation() {
@@ -488,8 +492,8 @@
* @see View#setTranslationX(float)
* @see #getTranslationX()
*/
- public void setTranslationX(float translationX) {
- nSetTranslationX(mNativeRenderNode, translationX);
+ public boolean setTranslationX(float translationX) {
+ return nSetTranslationX(mNativeRenderNode, translationX);
}
/**
@@ -509,8 +513,8 @@
* @see View#setTranslationY(float)
* @see #getTranslationY()
*/
- public void setTranslationY(float translationY) {
- nSetTranslationY(mNativeRenderNode, translationY);
+ public boolean setTranslationY(float translationY) {
+ return nSetTranslationY(mNativeRenderNode, translationY);
}
/**
@@ -528,8 +532,8 @@
* @see View#setTranslationZ(float)
* @see #getTranslationZ()
*/
- public void setTranslationZ(float translationZ) {
- nSetTranslationZ(mNativeRenderNode, translationZ);
+ public boolean setTranslationZ(float translationZ) {
+ return nSetTranslationZ(mNativeRenderNode, translationZ);
}
/**
@@ -549,8 +553,8 @@
* @see View#setRotation(float)
* @see #getRotation()
*/
- public void setRotation(float rotation) {
- nSetRotation(mNativeRenderNode, rotation);
+ public boolean setRotation(float rotation) {
+ return nSetRotation(mNativeRenderNode, rotation);
}
/**
@@ -570,8 +574,8 @@
* @see View#setRotationX(float)
* @see #getRotationX()
*/
- public void setRotationX(float rotationX) {
- nSetRotationX(mNativeRenderNode, rotationX);
+ public boolean setRotationX(float rotationX) {
+ return nSetRotationX(mNativeRenderNode, rotationX);
}
/**
@@ -591,8 +595,8 @@
* @see View#setRotationY(float)
* @see #getRotationY()
*/
- public void setRotationY(float rotationY) {
- nSetRotationY(mNativeRenderNode, rotationY);
+ public boolean setRotationY(float rotationY) {
+ return nSetRotationY(mNativeRenderNode, rotationY);
}
/**
@@ -612,8 +616,8 @@
* @see View#setScaleX(float)
* @see #getScaleX()
*/
- public void setScaleX(float scaleX) {
- nSetScaleX(mNativeRenderNode, scaleX);
+ public boolean setScaleX(float scaleX) {
+ return nSetScaleX(mNativeRenderNode, scaleX);
}
/**
@@ -633,8 +637,8 @@
* @see View#setScaleY(float)
* @see #getScaleY()
*/
- public void setScaleY(float scaleY) {
- nSetScaleY(mNativeRenderNode, scaleY);
+ public boolean setScaleY(float scaleY) {
+ return nSetScaleY(mNativeRenderNode, scaleY);
}
/**
@@ -654,8 +658,8 @@
* @see View#setPivotX(float)
* @see #getPivotX()
*/
- public void setPivotX(float pivotX) {
- nSetPivotX(mNativeRenderNode, pivotX);
+ public boolean setPivotX(float pivotX) {
+ return nSetPivotX(mNativeRenderNode, pivotX);
}
/**
@@ -675,8 +679,8 @@
* @see View#setPivotY(float)
* @see #getPivotY()
*/
- public void setPivotY(float pivotY) {
- nSetPivotY(mNativeRenderNode, pivotY);
+ public boolean setPivotY(float pivotY) {
+ return nSetPivotY(mNativeRenderNode, pivotY);
}
/**
@@ -702,8 +706,8 @@
* @see View#setCameraDistance(float)
* @see #getCameraDistance()
*/
- public void setCameraDistance(float distance) {
- nSetCameraDistance(mNativeRenderNode, distance);
+ public boolean setCameraDistance(float distance) {
+ return nSetCameraDistance(mNativeRenderNode, distance);
}
/**
@@ -723,8 +727,8 @@
* @see View#setLeft(int)
* @see #getLeft()
*/
- public void setLeft(int left) {
- nSetLeft(mNativeRenderNode, left);
+ public boolean setLeft(int left) {
+ return nSetLeft(mNativeRenderNode, left);
}
/**
@@ -744,8 +748,8 @@
* @see View#setTop(int)
* @see #getTop()
*/
- public void setTop(int top) {
- nSetTop(mNativeRenderNode, top);
+ public boolean setTop(int top) {
+ return nSetTop(mNativeRenderNode, top);
}
/**
@@ -765,8 +769,8 @@
* @see View#setRight(int)
* @see #getRight()
*/
- public void setRight(int right) {
- nSetRight(mNativeRenderNode, right);
+ public boolean setRight(int right) {
+ return nSetRight(mNativeRenderNode, right);
}
/**
@@ -786,8 +790,8 @@
* @see View#setBottom(int)
* @see #getBottom()
*/
- public void setBottom(int bottom) {
- nSetBottom(mNativeRenderNode, bottom);
+ public boolean setBottom(int bottom) {
+ return nSetBottom(mNativeRenderNode, bottom);
}
/**
@@ -812,8 +816,8 @@
* @see View#setRight(int)
* @see View#setBottom(int)
*/
- public void setLeftTopRightBottom(int left, int top, int right, int bottom) {
- nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom);
+ public boolean setLeftTopRightBottom(int left, int top, int right, int bottom) {
+ return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom);
}
/**
@@ -824,8 +828,8 @@
*
* @see View#offsetLeftAndRight(int)
*/
- public void offsetLeftAndRight(float offset) {
- nOffsetLeftAndRight(mNativeRenderNode, offset);
+ public boolean offsetLeftAndRight(float offset) {
+ return nOffsetLeftAndRight(mNativeRenderNode, offset);
}
/**
@@ -836,8 +840,8 @@
*
* @see View#offsetTopAndBottom(int)
*/
- public void offsetTopAndBottom(float offset) {
- nOffsetTopAndBottom(mNativeRenderNode, offset);
+ public boolean offsetTopAndBottom(float offset) {
+ return nOffsetTopAndBottom(mNativeRenderNode, offset);
}
/**
@@ -890,42 +894,43 @@
// Properties
- private static native void nOffsetTopAndBottom(long renderNode, float offset);
- private static native void nOffsetLeftAndRight(long renderNode, float offset);
- private static native void nSetLeftTopRightBottom(long renderNode, int left, int top,
+ private static native boolean nOffsetTopAndBottom(long renderNode, float offset);
+ private static native boolean nOffsetLeftAndRight(long renderNode, float offset);
+ private static native boolean nSetLeftTopRightBottom(long renderNode, int left, int top,
int right, int bottom);
- private static native void nSetBottom(long renderNode, int bottom);
- private static native void nSetRight(long renderNode, int right);
- private static native void nSetTop(long renderNode, int top);
- private static native void nSetLeft(long renderNode, int left);
- private static native void nSetCameraDistance(long renderNode, float distance);
- private static native void nSetPivotY(long renderNode, float pivotY);
- private static native void nSetPivotX(long renderNode, float pivotX);
- private static native void nSetCaching(long renderNode, boolean caching);
- private static native void nSetClipToBounds(long renderNode, boolean clipToBounds);
- private static native void nSetProjectBackwards(long renderNode, boolean shouldProject);
- private static native void nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
- private static native void nSetOutlineRoundRect(long renderNode, int left, int top,
+ private static native boolean nSetBottom(long renderNode, int bottom);
+ private static native boolean nSetRight(long renderNode, int right);
+ private static native boolean nSetTop(long renderNode, int top);
+ private static native boolean nSetLeft(long renderNode, int left);
+ private static native boolean nSetCameraDistance(long renderNode, float distance);
+ private static native boolean nSetPivotY(long renderNode, float pivotY);
+ private static native boolean nSetPivotX(long renderNode, float pivotX);
+ private static native boolean nSetLayerType(long renderNode, int layerType);
+ private static native boolean nSetLayerPaint(long renderNode, long paint);
+ private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
+ private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
+ private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
+ private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
int right, int bottom, float radius);
- private static native void nSetOutlineConvexPath(long renderNode, long nativePath);
- private static native void nSetOutlineEmpty(long renderNode);
- private static native void nSetClipToOutline(long renderNode, boolean clipToOutline);
- private static native void nSetRevealClip(long renderNode,
+ private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath);
+ private static native boolean nSetOutlineEmpty(long renderNode);
+ private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
+ private static native boolean nSetRevealClip(long renderNode,
boolean shouldClip, boolean inverseClip, float x, float y, float radius);
- private static native void nSetAlpha(long renderNode, float alpha);
- private static native void nSetHasOverlappingRendering(long renderNode,
+ private static native boolean nSetAlpha(long renderNode, float alpha);
+ private static native boolean nSetHasOverlappingRendering(long renderNode,
boolean hasOverlappingRendering);
- private static native void nSetElevation(long renderNode, float lift);
- private static native void nSetTranslationX(long renderNode, float translationX);
- private static native void nSetTranslationY(long renderNode, float translationY);
- private static native void nSetTranslationZ(long renderNode, float translationZ);
- private static native void nSetRotation(long renderNode, float rotation);
- private static native void nSetRotationX(long renderNode, float rotationX);
- private static native void nSetRotationY(long renderNode, float rotationY);
- private static native void nSetScaleX(long renderNode, float scaleX);
- private static native void nSetScaleY(long renderNode, float scaleY);
- private static native void nSetStaticMatrix(long renderNode, long nativeMatrix);
- private static native void nSetAnimationMatrix(long renderNode, long animationMatrix);
+ private static native boolean nSetElevation(long renderNode, float lift);
+ private static native boolean nSetTranslationX(long renderNode, float translationX);
+ private static native boolean nSetTranslationY(long renderNode, float translationY);
+ private static native boolean nSetTranslationZ(long renderNode, float translationZ);
+ private static native boolean nSetRotation(long renderNode, float rotation);
+ private static native boolean nSetRotationX(long renderNode, float rotationX);
+ private static native boolean nSetRotationY(long renderNode, float rotationY);
+ private static native boolean nSetScaleX(long renderNode, float scaleX);
+ private static native boolean nSetScaleY(long renderNode, float scaleY);
+ private static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix);
+ private static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix);
private static native boolean nHasOverlappingRendering(long renderNode);
private static native boolean nGetClipToOutline(long renderNode);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 5cd3d62..79f19b5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -78,8 +78,8 @@
IBinder displayToken);
private static native int nativeGetActiveConfig(IBinder displayToken);
private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
- private static native void nativeBlankDisplay(IBinder displayToken);
- private static native void nativeUnblankDisplay(IBinder displayToken);
+ private static native void nativeSetDisplayPowerMode(
+ IBinder displayToken, int mode);
private final CloseGuard mCloseGuard = CloseGuard.get();
@@ -209,6 +209,25 @@
*/
public static final int BUILT_IN_DISPLAY_ID_HDMI = 1;
+ /* Display power modes * /
+
+ /**
+ * Display power mode off: used while blanking the screen.
+ * Use only with {@link SurfaceControl#setDisplayPowerMode()}.
+ */
+ public static final int POWER_MODE_OFF = 0;
+
+ /**
+ * Display power mode doze: used while putting the screen into low power mode.
+ * Use only with {@link SurfaceControl#setDisplayPowerMode()}.
+ */
+ public static final int POWER_MODE_DOZE = 1;
+
+ /**
+ * Display power mode normal: used while unblanking the screen.
+ * Use only with {@link SurfaceControl#setDisplayPowerMode()}.
+ */
+ public static final int POWER_MODE_NORMAL = 2;
/**
@@ -487,18 +506,11 @@
}
}
- public static void unblankDisplay(IBinder displayToken) {
+ public static void setDisplayPowerMode(IBinder displayToken, int mode) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- nativeUnblankDisplay(displayToken);
- }
-
- public static void blankDisplay(IBinder displayToken) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- nativeBlankDisplay(displayToken);
+ nativeSetDisplayPowerMode(displayToken, mode);
}
public static SurfaceControl.PhysicalDisplayInfo[] getDisplayConfigs(IBinder displayToken) {
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 2a9f7d5..1f7eaa2 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -275,6 +275,11 @@
}
}
+ @Override
+ public void setLayerPaint(Paint paint) {
+ setLayerType(/* ignored */ 0, paint);
+ }
+
/**
* Always returns {@link #LAYER_TYPE_HARDWARE}.
*/
@@ -332,11 +337,6 @@
}
}
- @Override
- boolean destroyLayer(boolean valid) {
- return false;
- }
-
/**
* @hide
*/
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 11db996..2bfbd65 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -54,8 +54,6 @@
public class ThreadedRenderer extends HardwareRenderer {
private static final String LOGTAG = "ThreadedRenderer";
- private static final Rect NULL_RECT = new Rect();
-
// Keep in sync with DrawFrameTask.h SYNC_* flags
// Nothing interesting to report
private static final int SYNC_OK = 0x0;
@@ -228,7 +226,7 @@
}
@Override
- void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
+ void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
attachInfo.mIgnoreDirtyState = true;
long frameTimeNanos = mChoreographer.getFrameTimeNanos();
attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
@@ -246,12 +244,8 @@
attachInfo.mIgnoreDirtyState = false;
- if (dirty == null) {
- dirty = NULL_RECT;
- }
int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
- recordDuration, view.getResources().getDisplayMetrics().density,
- dirty.left, dirty.top, dirty.right, dirty.bottom);
+ recordDuration, view.getResources().getDisplayMetrics().density);
if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
attachInfo.mViewRootImpl.invalidate();
}
@@ -263,12 +257,6 @@
}
@Override
- HardwareLayer createDisplayListLayer(int width, int height) {
- long layer = nCreateDisplayListLayer(mNativeProxy, width, height);
- return HardwareLayer.adoptDisplayListLayer(this, layer);
- }
-
- @Override
HardwareLayer createTextureLayer() {
long layer = nCreateTextureLayer(mNativeProxy);
return HardwareLayer.adoptTextureLayer(this, layer);
@@ -331,6 +319,14 @@
}
}
+ static void startTrimMemory(int level) {
+ // TODO
+ }
+
+ static void endTrimMemory() {
+ // TODO
+ }
+
private static class AtlasInitializer {
static AtlasInitializer sInstance = new AtlasInitializer();
@@ -367,6 +363,8 @@
}
}
+ static native void setupShadersDiskCache(String cacheFile);
+
private static native void nSetAtlas(GraphicBuffer buffer, long[] map);
private static native long nCreateRootRenderNode();
@@ -383,8 +381,7 @@
float lightX, float lightY, float lightZ, float lightRadius);
private static native void nSetOpaque(long nativeProxy, boolean opaque);
private static native int nSyncAndDrawFrame(long nativeProxy,
- long frameTimeNanos, long recordDuration, float density,
- int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+ long frameTimeNanos, long recordDuration, float density);
private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
private static native void nDestroyCanvasAndSurface(long nativeProxy);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ce266d7..453ee17 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -431,7 +431,7 @@
* child. The child must use this size, and guarantee that all of its
* descendants will fit within this size.
* <li>AT_MOST: This is used by the parent to impose a maximum size on the
- * child. The child must gurantee that it and all of its descendants will fit
+ * child. The child must guarantee that it and all of its descendants will fit
* within this size.
* </ul>
* </p>
@@ -2775,6 +2775,13 @@
/**
* @hide
*
+ * Whether Recents is visible or not.
+ */
+ public static final int RECENT_APPS_VISIBLE = 0x00004000;
+
+ /**
+ * @hide
+ *
* Makes system ui transparent.
*/
public static final int SYSTEM_UI_TRANSPARENT = 0x00008000;
@@ -2782,7 +2789,7 @@
/**
* @hide
*/
- public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00007FFF;
+ public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FFF;
/**
* These are the system UI flags that can be cleared by events outside
@@ -3465,8 +3472,6 @@
})
int mLayerType = LAYER_TYPE_NONE;
Paint mLayerPaint;
- Rect mLocalDirtyRect;
- private HardwareLayer mHardwareLayer;
/**
* Set to true when drawing cache is enabled and cannot be created.
@@ -5377,8 +5382,9 @@
* Gets the location of this view in screen coordintates.
*
* @param outRect The output location
+ * @hide
*/
- void getBoundsOnScreen(Rect outRect) {
+ public void getBoundsOnScreen(Rect outRect) {
if (mAttachInfo == null) {
return;
}
@@ -6105,7 +6111,7 @@
// apply insets path and take things from there.
try {
mPrivateFlags3 |= PFLAG3_FITTING_SYSTEM_WINDOWS;
- return !dispatchApplyWindowInsets(new WindowInsets(insets)).hasInsets();
+ return dispatchApplyWindowInsets(new WindowInsets(insets)).isConsumed();
} finally {
mPrivateFlags3 &= ~PFLAG3_FITTING_SYSTEM_WINDOWS;
}
@@ -7648,7 +7654,7 @@
* notification is at at most once every
* {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}
* to avoid unnecessary load to the system. Also once a view has a pending
- * notifucation this method is a NOP until the notification has been sent.
+ * notification this method is a NOP until the notification has been sent.
*
* @hide
*/
@@ -9670,7 +9676,7 @@
/**
* The transform matrix of this view, which is calculated based on the current
- * roation, scale, and pivot properties.
+ * rotation, scale, and pivot properties.
*
* @see #getRotation()
* @see #getScaleX()
@@ -10521,13 +10527,18 @@
setTranslationZ(z - getElevation());
}
+ /**
+ * The base elevation of this view relative to its parent, in pixels.
+ *
+ * @return The base depth position of the view, in pixels.
+ */
@ViewDebug.ExportedProperty(category = "drawing")
public float getElevation() {
return mRenderNode.getElevation();
}
/**
- * Sets the base depth location of this view.
+ * Sets the base elevation of this view, in pixels.
*
* @attr ref android.R.styleable#View_elevation
*/
@@ -10633,24 +10644,6 @@
}
/**
- * Returns a ValueAnimator which can animate a clipping circle.
- * <p>
- * The View will be clipped to the animating circle.
- * <p>
- * Any shadow cast by the View will respect the circular clip from this animator.
- *
- * @param centerX The x coordinate of the center of the animating circle.
- * @param centerY The y coordinate of the center of the animating circle.
- * @param startRadius The starting radius of the animating circle.
- * @param endRadius The ending radius of the animating circle.
- */
- public final ValueAnimator createRevealAnimator(int centerX, int centerY,
- float startRadius, float endRadius) {
- return RevealAnimator.ofRevealCircle(this, centerX, centerY,
- startRadius, endRadius, false);
- }
-
- /**
* Returns a ValueAnimator which can animate a clearing circle.
* <p>
* The View is prevented from drawing within the circle, so the content
@@ -12943,7 +12936,6 @@
stopNestedScroll();
destroyDrawingCache();
- destroyLayer(false);
cleanupDraw();
mCurrentAnimation = null;
@@ -13421,30 +13413,32 @@
+ "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
}
- if (layerType == mLayerType) {
+ boolean typeChanged = mRenderNode.setLayerType(layerType);
+
+ if (!typeChanged) {
setLayerPaint(paint);
return;
}
// Destroy any previous software drawing cache if needed
- switch (mLayerType) {
- case LAYER_TYPE_HARDWARE:
- destroyLayer(false);
- // fall through - non-accelerated views may use software layer mechanism instead
- case LAYER_TYPE_SOFTWARE:
- destroyDrawingCache();
- break;
- default:
- break;
+ if (mLayerType == LAYER_TYPE_SOFTWARE) {
+ destroyDrawingCache();
+ invalidateParentCaches();
+ invalidate(true);
}
mLayerType = layerType;
- final boolean layerDisabled = mLayerType == LAYER_TYPE_NONE;
+ final boolean layerDisabled = (mLayerType == LAYER_TYPE_NONE);
mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint);
- mLocalDirtyRect = layerDisabled ? null : new Rect();
+ mRenderNode.setLayerPaint(mLayerPaint);
- invalidateParentCaches();
- invalidate(true);
+ if (mLayerType == LAYER_TYPE_SOFTWARE) {
+ // LAYER_TYPE_SOFTWARE is handled by View:draw(), so need to invalidate()
+ invalidateParentCaches();
+ invalidate(true);
+ } else {
+ invalidateViewProperty(false, false);
+ }
}
/**
@@ -13477,11 +13471,9 @@
if (layerType != LAYER_TYPE_NONE) {
mLayerPaint = paint == null ? new Paint() : paint;
if (layerType == LAYER_TYPE_HARDWARE) {
- HardwareLayer layer = getHardwareLayer();
- if (layer != null) {
- layer.setLayerPaint(mLayerPaint);
+ if (mRenderNode.setLayerPaint(mLayerPaint)) {
+ invalidateViewProperty(false, false);
}
- invalidateViewProperty(false, false);
} else {
invalidate();
}
@@ -13539,16 +13531,11 @@
switch (mLayerType) {
case LAYER_TYPE_HARDWARE:
- getHardwareLayer();
- // TODO: We need a better way to handle this case
- // If views have registered pre-draw listeners they need
- // to be notified before we build the layer. Those listeners
- // may however rely on other events to happen first so we
- // cannot just invoke them here until they don't cancel the
- // current frame
- if (!attachInfo.mTreeObserver.hasOnPreDrawListeners()) {
- attachInfo.mViewRootImpl.dispatchFlushHardwareLayerUpdates();
- }
+ // The only part of a hardware layer we can build in response to
+ // this call is to ensure the display list is up to date.
+ // The actual rendering of the display list into the layer must
+ // be done at playback time
+ updateDisplayListIfDirty();
break;
case LAYER_TYPE_SOFTWARE:
buildDrawingCache(true);
@@ -13557,76 +13544,13 @@
}
/**
- * <p>Returns a hardware layer that can be used to draw this view again
- * without executing its draw method.</p>
+ * If this View draws with a HardwareLayer, returns it.
+ * Otherwise returns null
*
- * @return A HardwareLayer ready to render, or null if an error occurred.
+ * TODO: Only TextureView uses this, can we eliminate it?
*/
HardwareLayer getHardwareLayer() {
- if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null ||
- !mAttachInfo.mHardwareRenderer.isEnabled()) {
- return null;
- }
-
- final int width = mRight - mLeft;
- final int height = mBottom - mTop;
-
- if (width == 0 || height == 0) {
- return null;
- }
-
- if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || mHardwareLayer == null) {
- if (mHardwareLayer == null) {
- mHardwareLayer = mAttachInfo.mHardwareRenderer.createDisplayListLayer(
- width, height);
- mLocalDirtyRect.set(0, 0, width, height);
- } else if (mHardwareLayer.isValid()) {
- // This should not be necessary but applications that change
- // the parameters of their background drawable without calling
- // this.setBackground(Drawable) can leave the view in a bad state
- // (for instance isOpaque() returns true, but the background is
- // not opaque.)
- computeOpaqueFlags();
-
- if (mHardwareLayer.prepare(width, height, isOpaque())) {
- mLocalDirtyRect.set(0, 0, width, height);
- }
- }
-
- // The layer is not valid if the underlying GPU resources cannot be allocated
- mHardwareLayer.flushChanges();
- if (!mHardwareLayer.isValid()) {
- return null;
- }
-
- mHardwareLayer.setLayerPaint(mLayerPaint);
- RenderNode displayList = mHardwareLayer.startRecording();
- updateDisplayListIfDirty(displayList, true);
- mHardwareLayer.endRecording(mLocalDirtyRect);
- mLocalDirtyRect.setEmpty();
- }
-
- return mHardwareLayer;
- }
-
- /**
- * Destroys this View's hardware layer if possible.
- *
- * @return True if the layer was destroyed, false otherwise.
- *
- * @see #setLayerType(int, android.graphics.Paint)
- * @see #LAYER_TYPE_HARDWARE
- */
- boolean destroyLayer(boolean valid) {
- if (mHardwareLayer != null) {
- mHardwareLayer.destroy();
- mHardwareLayer = null;
-
- invalidate(true);
- invalidateParentCaches();
- return true;
- }
- return false;
+ return null;
}
/**
@@ -13642,7 +13566,6 @@
*/
protected void destroyHardwareResources() {
resetDisplayList();
- destroyLayer(true);
}
/**
@@ -13737,20 +13660,8 @@
return !(mAttachInfo == null || mAttachInfo.mHardwareRenderer == null);
}
- /**
- * Returns a DisplayList. If the incoming displayList is null, one will be created.
- * Otherwise, the same display list will be returned (after having been rendered into
- * along the way, depending on the invalidation state of the view).
- *
- * @param renderNode The previous version of this displayList, could be null.
- * @param isLayer Whether the requester of the display list is a layer. If so,
- * the view will avoid creating a layer inside the resulting display list.
- * @return A new or reused DisplayList object.
- */
- private void updateDisplayListIfDirty(@NonNull RenderNode renderNode, boolean isLayer) {
- if (renderNode == null) {
- throw new IllegalArgumentException("RenderNode must not be null");
- }
+ private void updateDisplayListIfDirty() {
+ final RenderNode renderNode = mRenderNode;
if (!canHaveDisplayList()) {
// can't populate RenderNode, don't try
return;
@@ -13758,11 +13669,10 @@
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
|| !renderNode.isValid()
- || (!isLayer && mRecreateDisplayList)) {
+ || (mRecreateDisplayList)) {
// Don't need to recreate the display list, just need to tell our
// children to restore/recreate theirs
if (renderNode.isValid()
- && !isLayer
&& !mRecreateDisplayList) {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
@@ -13771,13 +13681,10 @@
return; // no work needed
}
- if (!isLayer) {
- // If we got here, we're recreating it. Mark it as such to ensure that
- // we copy in child display lists into ours in drawChild()
- mRecreateDisplayList = true;
- }
+ // If we got here, we're recreating it. Mark it as such to ensure that
+ // we copy in child display lists into ours in drawChild()
+ mRecreateDisplayList = true;
- boolean caching = false;
int width = mRight - mLeft;
int height = mBottom - mTop;
int layerType = getLayerType();
@@ -13785,34 +13692,21 @@
final HardwareCanvas canvas = renderNode.start(width, height);
try {
- if (!isLayer && layerType != LAYER_TYPE_NONE) {
- if (layerType == LAYER_TYPE_HARDWARE) {
- final HardwareLayer layer = getHardwareLayer();
- if (layer != null && layer.isValid()) {
- canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
- } else {
- canvas.saveLayer(0, 0, mRight - mLeft, mBottom - mTop, mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |
- Canvas.CLIP_TO_LAYER_SAVE_FLAG);
- }
- caching = true;
- } else {
- buildDrawingCache(true);
- Bitmap cache = getDrawingCache(true);
- if (cache != null) {
- canvas.drawBitmap(cache, 0, 0, mLayerPaint);
- caching = true;
- }
+ final HardwareLayer layer = getHardwareLayer();
+ if (layer != null && layer.isValid()) {
+ canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint);
+ } else if (layerType == LAYER_TYPE_SOFTWARE) {
+ buildDrawingCache(true);
+ Bitmap cache = getDrawingCache(true);
+ if (cache != null) {
+ canvas.drawBitmap(cache, 0, 0, mLayerPaint);
}
} else {
-
computeScroll();
canvas.translate(-mScrollX, -mScrollY);
- if (!isLayer) {
- mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
- mPrivateFlags &= ~PFLAG_DIRTY_MASK;
- }
+ mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
+ mPrivateFlags &= ~PFLAG_DIRTY_MASK;
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
@@ -13826,14 +13720,9 @@
}
} finally {
renderNode.end(canvas);
- renderNode.setCaching(caching);
- if (isLayer) {
- renderNode.setLeftTopRightBottom(0, 0, width, height);
- } else {
- setDisplayListProperties(renderNode);
- }
+ setDisplayListProperties(renderNode);
}
- } else if (!isLayer) {
+ } else {
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
@@ -13848,7 +13737,7 @@
* @hide
*/
public RenderNode getDisplayList() {
- updateDisplayListIfDirty(mRenderNode, false);
+ updateDisplayListIfDirty();
return mRenderNode;
}
@@ -16197,6 +16086,20 @@
}
/**
+ * Set this view's optical insets.
+ *
+ * <p>This method should be treated similarly to setMeasuredDimension and not as a general
+ * property. Views that compute their own optical insets should call it as part of measurement.
+ * This method does not request layout. If you are setting optical insets outside of
+ * measure/layout itself you will want to call requestLayout() yourself.
+ * </p>
+ * @hide
+ */
+ public void setOpticalInsets(Insets insets) {
+ mLayoutInsets = insets;
+ }
+
+ /**
* Changes the selection state of this view. A view can be selected or not.
* Note that selection is not the same as focus. Views are typically
* selected in the context of an AdapterView like ListView or GridView;
diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java
new file mode 100644
index 0000000..3854f34
--- /dev/null
+++ b/core/java/android/view/ViewAnimationUtils.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.animation.RevealAnimator;
+import android.animation.ValueAnimator;
+
+/**
+ * Defines common utilities for working with View's animations.
+ *
+ */
+public class ViewAnimationUtils {
+ private ViewAnimationUtils() {}
+ /**
+ * Returns a ValueAnimator which can animate a clipping circle.
+ *
+ * Any shadow cast by the View will respect the circular clip from this animator.
+ *
+ * @param view The View will be clipped to the animating circle.
+ * @param centerX The x coordinate of the center of the animating circle.
+ * @param centerY The y coordinate of the center of the animating circle.
+ * @param startRadius The starting radius of the animating circle.
+ * @param endRadius The ending radius of the animating circle.
+ */
+ public static final ValueAnimator createCircularReveal(View view,
+ int centerX, int centerY, float startRadius, float endRadius) {
+ return RevealAnimator.ofRevealCircle(view, centerX, centerY,
+ startRadius, endRadius, false);
+ }
+}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0f40ee7..a02e76b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4386,10 +4386,9 @@
// Make sure we do not set both flags at the same time
int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
- if (child.mLayerType != LAYER_TYPE_NONE) {
+ if (child.mLayerType == LAYER_TYPE_SOFTWARE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
- child.mLocalDirtyRect.union(dirty);
}
final int[] location = attachInfo.mInvalidateChildLocation;
@@ -4498,9 +4497,8 @@
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
- if (mLayerType != LAYER_TYPE_NONE) {
+ if (mLayerType == LAYER_TYPE_SOFTWARE) {
mPrivateFlags |= PFLAG_INVALIDATED;
- mLocalDirtyRect.union(dirty);
}
return mParent;
@@ -4517,9 +4515,8 @@
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
- if (mLayerType != LAYER_TYPE_NONE) {
+ if (mLayerType == LAYER_TYPE_SOFTWARE) {
mPrivateFlags |= PFLAG_INVALIDATED;
- mLocalDirtyRect.union(dirty);
}
return mParent;
@@ -4530,6 +4527,28 @@
}
/**
+ * Native-calculated damage path
+ * Returns false if this path was unable to complete successfully. This means
+ * it hit a ViewParent it doesn't recognize and needs to fall back to calculating
+ * damage area
+ * @hide
+ */
+ public boolean damageChildDeferred(View child) {
+ ViewParent parent = getParent();
+ while (parent != null) {
+ if (parent instanceof ViewGroup) {
+ parent = parent.getParent();
+ } else if (parent instanceof ViewRootImpl) {
+ ((ViewRootImpl) parent).invalidate();
+ return true;
+ } else {
+ parent = null;
+ }
+ }
+ return false;
+ }
+
+ /**
* Quick invalidation method called by View.invalidateViewProperty. This doesn't set the
* DRAWN flags and doesn't handle the Animation logic that the default invalidation methods
* do; all we want to do here is schedule a traversal with the appropriate dirty rect.
@@ -4537,14 +4556,14 @@
* @hide
*/
public void damageChild(View child, final Rect dirty) {
+ if (damageChildDeferred(child)) {
+ return;
+ }
+
ViewParent parent = this;
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
- if (child.mLayerType != LAYER_TYPE_NONE) {
- child.mLocalDirtyRect.union(dirty);
- }
-
int left = child.mLeft;
int top = child.mTop;
if (!child.getMatrix().isIdentity()) {
@@ -4592,9 +4611,6 @@
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 ||
dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
- if (mLayerType != LAYER_TYPE_NONE) {
- mLocalDirtyRect.union(dirty);
- }
if (!getMatrix().isIdentity()) {
transformRect(dirty);
}
@@ -5574,11 +5590,11 @@
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
insets = super.dispatchApplyWindowInsets(insets);
- if (insets.hasInsets()) {
+ if (!insets.isConsumed()) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
insets = getChildAt(i).dispatchApplyWindowInsets(insets);
- if (!insets.hasInsets()) {
+ if (insets.isConsumed()) {
break;
}
}
@@ -6913,13 +6929,9 @@
if (getClass() != another.getClass()) {
return 1;
}
- // First is above second.
- if (mLocation.bottom - another.mLocation.top <= 0) {
- return -1;
- }
- // First is below second.
- if (mLocation.top - another.mLocation.bottom >= 0) {
- return 1;
+ final int topDiference = mLocation.top - another.mLocation.top;
+ if (topDiference != 0) {
+ return topDiference;
}
// LTR
if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
@@ -6935,11 +6947,6 @@
return -rightDifference;
}
}
- // Break tie by top.
- final int topDiference = mLocation.top - another.mLocation.top;
- if (topDiference != 0) {
- return topDiference;
- }
// Break tie by height.
final int heightDiference = mLocation.height() - another.mLocation.height();
if (heightDiference != 0) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ac25b57..4cd1b25 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -181,7 +181,6 @@
int mWidth;
int mHeight;
Rect mDirty;
- final Rect mCurrentDirty = new Rect();
boolean mIsAnimating;
CompatibilityInfo.Translator mTranslator;
@@ -632,27 +631,7 @@
}
void destroyHardwareLayers() {
- if (mThread != Thread.currentThread()) {
- if (mAttachInfo.mHardwareRenderer != null &&
- mAttachInfo.mHardwareRenderer.isEnabled()) {
- HardwareRenderer.trimMemory(ComponentCallbacks2.TRIM_MEMORY_MODERATE);
- }
- } else {
- destroyHardwareLayer(mView);
- }
- }
-
- private static void destroyHardwareLayer(View view) {
- view.destroyLayer(true);
-
- if (view instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) view;
-
- int count = group.getChildCount();
- for (int i = 0; i < count; i++) {
- destroyHardwareLayer(group.getChildAt(i));
- }
- }
+ // TODO Implement
}
void flushHardwareLayerUpdates() {
@@ -716,17 +695,6 @@
if (!HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled
&& forceHwAccelerated)) {
- 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) {
mAttachInfo.mHardwareRenderer.destroy(true);
}
@@ -872,7 +840,9 @@
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
- scheduleTraversals();
+ if (!mWillDrawSoon) {
+ scheduleTraversals();
+ }
}
void invalidateWorld(View view) {
@@ -1530,54 +1500,55 @@
disposeResizeBuffer();
- if (mResizeBuffer == null) {
- mResizeBuffer = mAttachInfo.mHardwareRenderer.createDisplayListLayer(
- mWidth, mHeight);
- }
- mResizeBuffer.prepare(mWidth, mHeight, false);
- RenderNode layerRenderNode = mResizeBuffer.startRecording();
- HardwareCanvas layerCanvas = layerRenderNode.start(mWidth, mHeight);
- try {
- final int restoreCount = layerCanvas.save();
-
- int yoff;
- final boolean scrolling = mScroller != null
- && mScroller.computeScrollOffset();
- if (scrolling) {
- yoff = mScroller.getCurrY();
- mScroller.abortAnimation();
- } else {
- yoff = mScrollY;
- }
-
- layerCanvas.translate(0, -yoff);
- if (mTranslator != null) {
- mTranslator.translateCanvas(layerCanvas);
- }
-
- RenderNode renderNode = mView.mRenderNode;
- if (renderNode != null && renderNode.isValid()) {
- layerCanvas.drawDisplayList(renderNode, null,
- RenderNode.FLAG_CLIP_CHILDREN);
- } else {
- mView.draw(layerCanvas);
- }
-
- drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
-
- mResizeBufferStartTime = SystemClock.uptimeMillis();
- mResizeBufferDuration = mView.getResources().getInteger(
- com.android.internal.R.integer.config_mediumAnimTime);
-
- layerCanvas.restoreToCount(restoreCount);
- layerRenderNode.end(layerCanvas);
- layerRenderNode.setCaching(true);
- layerRenderNode.setLeftTopRightBottom(0, 0, mWidth, mHeight);
- mTempRect.set(0, 0, mWidth, mHeight);
- } finally {
- mResizeBuffer.endRecording(mTempRect);
- }
- mAttachInfo.mHardwareRenderer.flushLayerUpdates();
+// TODO: Again....
+// if (mResizeBuffer == null) {
+// mResizeBuffer = mAttachInfo.mHardwareRenderer.createDisplayListLayer(
+// mWidth, mHeight);
+// }
+// mResizeBuffer.prepare(mWidth, mHeight, false);
+// RenderNode layerRenderNode = mResizeBuffer.startRecording();
+// HardwareCanvas layerCanvas = layerRenderNode.start(mWidth, mHeight);
+// try {
+// final int restoreCount = layerCanvas.save();
+//
+// int yoff;
+// final boolean scrolling = mScroller != null
+// && mScroller.computeScrollOffset();
+// if (scrolling) {
+// yoff = mScroller.getCurrY();
+// mScroller.abortAnimation();
+// } else {
+// yoff = mScrollY;
+// }
+//
+// layerCanvas.translate(0, -yoff);
+// if (mTranslator != null) {
+// mTranslator.translateCanvas(layerCanvas);
+// }
+//
+// RenderNode renderNode = mView.mRenderNode;
+// if (renderNode != null && renderNode.isValid()) {
+// layerCanvas.drawDisplayList(renderNode, null,
+// RenderNode.FLAG_CLIP_CHILDREN);
+// } else {
+// mView.draw(layerCanvas);
+// }
+//
+// drawAccessibilityFocusedDrawableIfNeeded(layerCanvas);
+//
+// mResizeBufferStartTime = SystemClock.uptimeMillis();
+// mResizeBufferDuration = mView.getResources().getInteger(
+// com.android.internal.R.integer.config_mediumAnimTime);
+//
+// layerCanvas.restoreToCount(restoreCount);
+// layerRenderNode.end(layerCanvas);
+// layerRenderNode.setCaching(true);
+// layerRenderNode.setLeftTopRightBottom(0, 0, mWidth, mHeight);
+// mTempRect.set(0, 0, mWidth, mHeight);
+// } finally {
+// mResizeBuffer.endRecording(mTempRect);
+// }
+// mAttachInfo.mHardwareRenderer.flushLayerUpdates();
}
mAttachInfo.mContentInsets.set(mPendingContentInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
@@ -2447,12 +2418,10 @@
mHardwareYOffset = yoff;
mResizeAlpha = resizeAlpha;
- mCurrentDirty.set(dirty);
dirty.setEmpty();
mBlockResizeBuffer = false;
- attachInfo.mHardwareRenderer.draw(mView, attachInfo, this,
- animating ? null : mCurrentDirty);
+ attachInfo.mHardwareRenderer.draw(mView, attachInfo, this);
} else {
// If we get here with a disabled & requested hardware renderer, something went
// wrong (an invalidate posted right before we destroyed the hardware surface
@@ -6227,7 +6196,7 @@
mTempRect.offset(0, -mCurScrollY);
mTempRect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
try {
- mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect, immediate);
+ mWindowSession.onRectangleOnScreenRequested(mWindow, mTempRect);
} catch (RemoteException re) {
/* ignore */
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index ecc4586..0120875 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1516,6 +1516,29 @@
public boolean getAllowExitTransitionOverlap() { return true; }
/**
+ * Returns the duration, in milliseconds, of the window background fade
+ * when transitioning into or away from an Activity when called with an Activity Transition.
+ * <p>When executing the enter transition, the background starts transparent
+ * and fades in. This requires {@link #FEATURE_CONTENT_TRANSITIONS}. The default is
+ * 300 milliseconds.</p>
+ * @return The duration of the window background fade to opaque during enter transition.
+ * @see #getEnterTransition()
+ */
+ public long getTransitionBackgroundFadeDuration() { return 0; }
+
+ /**
+ * Sets the duration, in milliseconds, of the window background fade
+ * when transitioning into or away from an Activity when called with an Activity Transition.
+ * <p>When executing the enter transition, the background starts transparent
+ * and fades in. This requires {@link #FEATURE_CONTENT_TRANSITIONS}. The default is
+ * 300 milliseconds.</p>
+ * @param fadeDurationMillis The duration of the window background fade to or from opaque
+ * during enter transition.
+ * @see #setEnterTransition(android.transition.Transition)
+ */
+ public void setTransitionBackgroundFadeDuration(long fadeDurationMillis) { }
+
+ /**
* @return the color of the status bar.
*/
public abstract int getStatusBarColor();
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
index 7f89044..b721074 100644
--- a/core/java/android/view/WindowInfo.java
+++ b/core/java/android/view/WindowInfo.java
@@ -111,6 +111,7 @@
builder.append("type=").append(type);
builder.append(", layer=").append(layer);
builder.append(", token=").append(token);
+ builder.append(", bounds=").append(boundsInScreen);
builder.append(", parent=").append(parentToken);
builder.append(", focused=").append(focused);
builder.append(", children=").append(childTokens);
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 294f472..1d2f1bf 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -35,6 +35,9 @@
private Rect mTempRect;
private boolean mIsRound;
+ private boolean mSystemWindowInsetsConsumed = false;
+ private boolean mWindowDecorInsetsConsumed = false;
+
private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
/**
@@ -43,7 +46,13 @@
* since it would allow them to inadvertently consume unknown insets by returning it.
* @hide
*/
- public static final WindowInsets EMPTY = new WindowInsets(EMPTY_RECT, EMPTY_RECT);
+ public static final WindowInsets CONSUMED;
+
+ static {
+ CONSUMED = new WindowInsets(EMPTY_RECT, EMPTY_RECT);
+ CONSUMED.mSystemWindowInsetsConsumed = true;
+ CONSUMED.mWindowDecorInsetsConsumed = true;
+ }
/** @hide */
public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) {
@@ -52,13 +61,17 @@
/** @hide */
public WindowInsets(Rect systemWindowInsets, boolean isRound) {
- this(systemWindowInsets, EMPTY_RECT, isRound);
+ this(systemWindowInsets, null, isRound);
}
/** @hide */
public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) {
- mSystemWindowInsets = systemWindowInsets;
- mWindowDecorInsets = windowDecorInsets;
+ mSystemWindowInsetsConsumed = systemWindowInsets == null;
+ mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets;
+
+ mWindowDecorInsetsConsumed = windowDecorInsets == null;
+ mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets;
+
mIsRound = isRound;
}
@@ -70,12 +83,14 @@
public WindowInsets(WindowInsets src) {
mSystemWindowInsets = src.mSystemWindowInsets;
mWindowDecorInsets = src.mWindowDecorInsets;
+ mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed;
+ mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed;
mIsRound = src.mIsRound;
}
/** @hide */
public WindowInsets(Rect systemWindowInsets) {
- this(systemWindowInsets, EMPTY_RECT);
+ this(systemWindowInsets, null);
}
/**
@@ -87,7 +102,12 @@
if (mTempRect == null) {
mTempRect = new Rect();
}
- mTempRect.set(mSystemWindowInsets);
+ if (mSystemWindowInsets != null) {
+ mTempRect.set(mSystemWindowInsets);
+ } else {
+ // If there were no system window insets, this is just empty.
+ mTempRect.setEmpty();
+ }
return mTempRect;
}
@@ -238,6 +258,24 @@
}
/**
+ * Check if these insets have been fully consumed.
+ *
+ * <p>Insets are considered "consumed" if the applicable <code>consume*</code> methods
+ * have been called such that all insets have been set to zero. This affects propagation of
+ * insets through the view hierarchy; insets that have not been fully consumed will continue
+ * to propagate down to child views.</p>
+ *
+ * <p>The result of this method is equivalent to the return value of
+ * {@link View#fitSystemWindows(android.graphics.Rect)}.</p>
+ *
+ * @return true if the insets have been fully consumed.
+ * @hide Pending API
+ */
+ public boolean isConsumed() {
+ return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed;
+ }
+
+ /**
* Returns true if the associated window has a round shape.
*
* <p>A round window's left, top, right and bottom edges reach all the way to the
@@ -258,7 +296,8 @@
*/
public WindowInsets consumeSystemWindowInsets() {
final WindowInsets result = new WindowInsets(this);
- result.mSystemWindowInsets = new Rect(0, 0, 0, 0);
+ result.mSystemWindowInsets = EMPTY_RECT;
+ result.mSystemWindowInsetsConsumed = true;
return result;
}
@@ -276,10 +315,12 @@
boolean right, boolean bottom) {
if (left || top || right || bottom) {
final WindowInsets result = new WindowInsets(this);
- result.mSystemWindowInsets = new Rect(left ? 0 : mSystemWindowInsets.left,
+ result.mSystemWindowInsets = new Rect(
+ left ? 0 : mSystemWindowInsets.left,
top ? 0 : mSystemWindowInsets.top,
right ? 0 : mSystemWindowInsets.right,
bottom ? 0 : mSystemWindowInsets.bottom);
+ result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
return result;
}
return this;
@@ -299,6 +340,7 @@
int right, int bottom) {
final WindowInsets result = new WindowInsets(this);
result.mSystemWindowInsets = new Rect(left, top, right, bottom);
+ result.mSystemWindowInsetsConsumed = !hasSystemWindowInsets();
return result;
}
@@ -308,6 +350,7 @@
public WindowInsets consumeWindowDecorInsets() {
final WindowInsets result = new WindowInsets(this);
result.mWindowDecorInsets.set(0, 0, 0, 0);
+ result.mWindowDecorInsetsConsumed = true;
return result;
}
@@ -322,6 +365,7 @@
top ? 0 : mWindowDecorInsets.top,
right ? 0 : mWindowDecorInsets.right,
bottom ? 0 : mWindowDecorInsets.bottom);
+ result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets();
return result;
}
return this;
@@ -333,6 +377,7 @@
public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) {
final WindowInsets result = new WindowInsets(this);
result.mWindowDecorInsets = new Rect(left, top, right, bottom);
+ result.mWindowDecorInsetsConsumed = !hasWindowDecorInsets();
return result;
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index b4779f4..0ebf2e1 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -147,9 +147,14 @@
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
+ new IWindowSessionCallback.Stub() {
+ @Override
+ public void onAnimatorScaleChanged(float scale) {
+ ValueAnimator.setDurationScale(scale);
+ }
+ },
imm.getClient(), imm.getInputContext());
- float animatorScale = windowManager.getAnimationScale(2);
- ValueAnimator.setDurationScale(animatorScale);
+ ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 14dc356..a92bf59 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -20,6 +20,7 @@
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
import android.os.IBinder;
+import android.os.IRemoteCallback;
import java.util.List;
@@ -105,7 +106,7 @@
* Set by the accessibility layer to specify the magnification and panning to
* be applied to all windows that should be magnified.
*
- * @param callbacks The callbacks to invoke.
+ * @param spec The MagnficationSpec to set.
*
* @see #setMagnificationCallbacks(MagnificationCallbacks)
*/
@@ -161,4 +162,10 @@
* @param outBounds The frame to populate.
*/
public abstract void getWindowFrame(IBinder token, Rect outBounds);
+
+ /**
+ * Invalidate all visible windows. Then report back on the callback once all windows have
+ * redrawn.
+ */
+ public abstract void waitForAllWindowsDrawn(IRemoteCallback callback, long timeout);
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 2b4677c..d426edc 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -605,21 +605,21 @@
public int getConfigDisplayHeight(int fullWidth, int fullHeight, int rotation);
/**
- * Return whether the given window should forcibly hide everything
- * behind it. Typically returns true for the keyguard.
+ * Return whether the given window is forcibly hiding all windows except windows with
+ * FLAG_SHOW_WHEN_LOCKED set. Typically returns true for the keyguard.
*/
- public boolean doesForceHide(WindowManager.LayoutParams attrs);
+ public boolean isForceHiding(WindowManager.LayoutParams attrs);
/**
- * Return whether the given window can become one that passes doesForceHide() test.
+ * Return whether the given window can become one that passes isForceHiding() test.
* Typically returns true for the StatusBar.
*/
public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs);
/**
* Determine if a window that is behind one that is force hiding
- * (as determined by {@link #doesForceHide}) should actually be hidden.
+ * (as determined by {@link #isForceHiding}) should actually be hidden.
* For example, typically returns false for the status bar. Be careful
* to return false for any window that you may hide yourself, since this
* will conflict with what you set.
@@ -830,13 +830,11 @@
* setting the window's frame, either here or in finishLayout().
*
* @param win The window being positioned.
- * @param attrs The LayoutParams of the window.
* @param attached For sub-windows, the window it is attached to; this
* window will already have had layoutWindow() called on it
* so you can use its Rect. Otherwise null.
*/
- public void layoutWindowLw(WindowState win,
- WindowManager.LayoutParams attrs, WindowState attached);
+ public void layoutWindowLw(WindowState win, WindowState attached);
/**
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 5b9372d..4748402 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -225,17 +225,11 @@
try {
IAccessibilityServiceConnection connection = getConnection(connectionId);
if (connection != null) {
- List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
- if (windows != null) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Window cache hit");
- }
- return windows;
- }
- if (DEBUG) {
- Log.i(LOG_TAG, "Window cache miss");
- }
- windows = connection.getWindows();
+ // The system is just sending data for windows that we introspected
+ // and changed but not ones that appeared, so we have to always call
+ // into the system process. This is less expensice as opposed to
+ // sending all windows on every window change.
+ List<AccessibilityWindowInfo> windows = connection.getWindows();
if (windows != null) {
final int windowCount = windows.size();
for (int i = 0; i < windowCount; i++) {
diff --git a/core/java/android/view/accessibility/CaptioningManager.java b/core/java/android/view/accessibility/CaptioningManager.java
index a0134d6..334ff43 100644
--- a/core/java/android/view/accessibility/CaptioningManager.java
+++ b/core/java/android/view/accessibility/CaptioningManager.java
@@ -16,6 +16,8 @@
package android.view.accessibility;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
@@ -78,6 +80,7 @@
* language
* @hide
*/
+ @Nullable
public final String getRawLocale() {
return Secure.getString(mContentResolver, Secure.ACCESSIBILITY_CAPTIONING_LOCALE);
}
@@ -86,6 +89,7 @@
* @return the locale for the user's preferred captioning language, or null
* if not specified
*/
+ @Nullable
public final Locale getLocale() {
final String rawLocale = getRawLocale();
if (!TextUtils.isEmpty(rawLocale)) {
@@ -125,6 +129,7 @@
* @return the user's preferred visual properties for captions as a
* {@link CaptionStyle}, or the default style if not specified
*/
+ @NonNull
public CaptionStyle getUserStyle() {
final int preset = getRawUserStyle();
if (preset == CaptionStyle.PRESET_CUSTOM) {
@@ -140,17 +145,19 @@
*
* @param listener the listener to add
*/
- public void addCaptioningChangeListener(CaptioningChangeListener listener) {
+ public void addCaptioningChangeListener(@NonNull CaptioningChangeListener listener) {
synchronized (mListeners) {
if (mListeners.isEmpty()) {
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_ENABLED);
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR);
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR);
+ registerObserver(Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR);
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_EDGE_TYPE);
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR);
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE);
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE);
registerObserver(Secure.ACCESSIBILITY_CAPTIONING_LOCALE);
+ registerObserver(Secure.ACCESSIBILITY_CAPTIONING_PRESET);
}
mListeners.add(listener);
@@ -167,7 +174,7 @@
*
* @param listener the listener to remove
*/
- public void removeCaptioningChangeListener(CaptioningChangeListener listener) {
+ public void removeCaptioningChangeListener(@NonNull CaptioningChangeListener listener) {
synchronized (mListeners) {
mListeners.remove(listener);
@@ -253,11 +260,18 @@
/** Packed value for a color of 'none' and a cached opacity of 100%. */
private static final int COLOR_NONE_OPAQUE = 0x000000FF;
+ /** Packed value for an unspecified color and opacity. */
+ private static final int COLOR_UNSPECIFIED = 0x000001FF;
+
private static final CaptionStyle WHITE_ON_BLACK;
private static final CaptionStyle BLACK_ON_WHITE;
private static final CaptionStyle YELLOW_ON_BLACK;
private static final CaptionStyle YELLOW_ON_BLUE;
private static final CaptionStyle DEFAULT_CUSTOM;
+ private static final CaptionStyle UNSPECIFIED;
+
+ /** The default caption style used to fill in unspecified values. @hide */
+ public static final CaptionStyle DEFAULT;
/** @hide */
public static final CaptionStyle[] PRESETS;
@@ -265,6 +279,9 @@
/** @hide */
public static final int PRESET_CUSTOM = -1;
+ /** Unspecified edge type value. */
+ public static final int EDGE_TYPE_UNSPECIFIED = -1;
+
/** Edge type value specifying no character edges. */
public static final int EDGE_TYPE_NONE = 0;
@@ -289,6 +306,7 @@
/**
* The preferred edge type for video captions, one of:
* <ul>
+ * <li>{@link #EDGE_TYPE_UNSPECIFIED}
* <li>{@link #EDGE_TYPE_NONE}
* <li>{@link #EDGE_TYPE_OUTLINE}
* <li>{@link #EDGE_TYPE_DROP_SHADOW}
@@ -326,9 +344,81 @@
}
/**
+ * Applies a caption style, overriding any properties that are specified
+ * in the overlay caption.
+ *
+ * @param overlay The style to apply
+ * @return A caption style with the overlay style applied
+ * @hide
+ */
+ @NonNull
+ public CaptionStyle applyStyle(@NonNull CaptionStyle overlay) {
+ final int newForegroundColor = overlay.hasForegroundColor() ?
+ overlay.foregroundColor : foregroundColor;
+ final int newBackgroundColor = overlay.hasBackgroundColor() ?
+ overlay.backgroundColor : backgroundColor;
+ final int newEdgeType = overlay.hasEdgeType() ?
+ overlay.edgeType : edgeType;
+ final int newEdgeColor = overlay.hasEdgeColor() ?
+ overlay.edgeColor : edgeColor;
+ final int newWindowColor = overlay.hasWindowColor() ?
+ overlay.windowColor : windowColor;
+ final String newRawTypeface = overlay.mRawTypeface != null ?
+ overlay.mRawTypeface : mRawTypeface;
+ return new CaptionStyle(newForegroundColor, newBackgroundColor, newEdgeType,
+ newEdgeColor, newWindowColor, newRawTypeface);
+ }
+
+ /**
+ * @return {@code true} if the user has specified a background color
+ * that should override the application default, {@code false}
+ * otherwise
+ */
+ public boolean hasBackgroundColor() {
+ return backgroundColor != COLOR_UNSPECIFIED;
+ }
+
+ /**
+ * @return {@code true} if the user has specified a foreground color
+ * that should override the application default, {@code false}
+ * otherwise
+ */
+ public boolean hasForegroundColor() {
+ return foregroundColor != COLOR_UNSPECIFIED;
+ }
+
+ /**
+ * @return {@code true} if the user has specified an edge type that
+ * should override the application default, {@code false}
+ * otherwise
+ */
+ public boolean hasEdgeType() {
+ return edgeType != EDGE_TYPE_UNSPECIFIED;
+ }
+
+ /**
+ * @return {@code true} if the user has specified an edge color that
+ * should override the application default, {@code false}
+ * otherwise
+ */
+ public boolean hasEdgeColor() {
+ return edgeColor != COLOR_UNSPECIFIED;
+ }
+
+ /**
+ * @return {@code true} if the user has specified a window color that
+ * should override the application default, {@code false}
+ * otherwise
+ */
+ public boolean hasWindowColor() {
+ return windowColor != COLOR_UNSPECIFIED;
+ }
+
+ /**
* @return the preferred {@link Typeface} for video captions, or null if
* not specified
*/
+ @Nullable
public Typeface getTypeface() {
if (mParsedTypeface == null && !TextUtils.isEmpty(mRawTypeface)) {
mParsedTypeface = Typeface.create(mRawTypeface, Typeface.NORMAL);
@@ -339,6 +429,7 @@
/**
* @hide
*/
+ @NonNull
public static CaptionStyle getCustomStyle(ContentResolver cr) {
final CaptionStyle defStyle = CaptionStyle.DEFAULT_CUSTOM;
final int foregroundColor = Secure.getInt(
@@ -370,12 +461,17 @@
Color.BLACK, COLOR_NONE_OPAQUE, null);
YELLOW_ON_BLUE = new CaptionStyle(Color.YELLOW, Color.BLUE, EDGE_TYPE_NONE,
Color.BLACK, COLOR_NONE_OPAQUE, null);
+ UNSPECIFIED = new CaptionStyle(COLOR_UNSPECIFIED, COLOR_UNSPECIFIED,
+ EDGE_TYPE_UNSPECIFIED, COLOR_UNSPECIFIED, COLOR_UNSPECIFIED, null);
+ // The ordering of these cannot change since we store the index
+ // directly in preferences.
PRESETS = new CaptionStyle[] {
- WHITE_ON_BLACK, BLACK_ON_WHITE, YELLOW_ON_BLACK, YELLOW_ON_BLUE
+ WHITE_ON_BLACK, BLACK_ON_WHITE, YELLOW_ON_BLACK, YELLOW_ON_BLUE, UNSPECIFIED
};
DEFAULT_CUSTOM = WHITE_ON_BLACK;
+ DEFAULT = WHITE_ON_BLACK;
}
}
@@ -389,8 +485,7 @@
*
* @param enabled the user's new preferred captioning enabled state
*/
- public void onEnabledChanged(boolean enabled) {
- }
+ public void onEnabledChanged(boolean enabled) {}
/**
* Called when the captioning user style changes.
@@ -398,17 +493,15 @@
* @param userStyle the user's new preferred style
* @see CaptioningManager#getUserStyle()
*/
- public void onUserStyleChanged(CaptionStyle userStyle) {
- }
+ public void onUserStyleChanged(@NonNull CaptionStyle userStyle) {}
/**
* Called when the captioning locale changes.
*
- * @param locale the preferred captioning locale
+ * @param locale the preferred captioning locale, or {@code null} if not specified
* @see CaptioningManager#getLocale()
*/
- public void onLocaleChanged(Locale locale) {
- }
+ public void onLocaleChanged(@Nullable Locale locale) {}
/**
* Called when the captioning font scaling factor changes.
@@ -416,7 +509,6 @@
* @param fontScale the preferred font scaling factor
* @see CaptioningManager#getFontScale()
*/
- public void onFontScaleChanged(float fontScale) {
- }
+ public void onFontScaleChanged(float fontScale) {}
}
}
diff --git a/core/java/android/view/animation/AccelerateInterpolator.java b/core/java/android/view/animation/AccelerateInterpolator.java
index c08f348..1c75f16 100644
--- a/core/java/android/view/animation/AccelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateInterpolator.java
@@ -17,15 +17,18 @@
package android.view.animation;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import com.android.internal.R;
import com.android.internal.view.animation.HasNativeInterpolator;
import com.android.internal.view.animation.NativeInterpolatorFactory;
import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
- * An interpolator where the rate of change starts out slowly and
+ * An interpolator where the rate of change starts out slowly and
* and then accelerates.
*
*/
@@ -38,10 +41,10 @@
mFactor = 1.0f;
mDoubleFactor = 2.0;
}
-
+
/**
* Constructor
- *
+ *
* @param factor Degree to which the animation should be eased. Seting
* factor to 1.0f produces a y=x^2 parabola. Increasing factor above
* 1.0f exaggerates the ease-in effect (i.e., it starts even
@@ -51,17 +54,26 @@
mFactor = factor;
mDoubleFactor = 2 * mFactor;
}
-
+
public AccelerateInterpolator(Context context, AttributeSet attrs) {
- TypedArray a =
- context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AccelerateInterpolator);
-
- mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f);
+ this(context.getResources(), context.getTheme(), attrs);
+ }
+
+ /** @hide */
+ public AccelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.AccelerateInterpolator, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, R.styleable.AccelerateInterpolator);
+ }
+
+ mFactor = a.getFloat(R.styleable.AccelerateInterpolator_factor, 1.0f);
mDoubleFactor = 2 * mFactor;
a.recycle();
}
-
+
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 1d1fa1e..af4e04f 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -20,6 +20,8 @@
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.XmlResourceParser;
import android.content.res.Resources.NotFoundException;
import android.util.AttributeSet;
@@ -143,7 +145,7 @@
*/
public static LayoutAnimationController loadLayoutAnimation(Context context, int id)
throws NotFoundException {
-
+
XmlResourceParser parser = null;
try {
parser = context.getResources().getAnimation(id);
@@ -201,7 +203,7 @@
/**
* Make an animation for objects becoming visible. Uses a slide and fade
* effect.
- *
+ *
* @param c Context for loading resources
* @param fromLeft is the object to be animated coming from the left
* @return The new animation
@@ -218,11 +220,11 @@
a.setStartTime(currentAnimationTimeMillis());
return a;
}
-
+
/**
* Make an animation for objects becoming invisible. Uses a slide and fade
* effect.
- *
+ *
* @param c Context for loading resources
* @param toRight is the object to be animated exiting to the right
* @return The new animation
@@ -234,17 +236,17 @@
} else {
a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_left);
}
-
+
a.setInterpolator(new AccelerateInterpolator());
a.setStartTime(currentAnimationTimeMillis());
return a;
}
-
+
/**
* Make an animation for objects becoming visible. Uses a slide up and fade
* effect.
- *
+ *
* @param c Context for loading resources
* @return The new animation
*/
@@ -255,10 +257,10 @@
a.setStartTime(currentAnimationTimeMillis());
return a;
}
-
+
/**
* Loads an {@link Interpolator} object from a resource
- *
+ *
* @param context Application context used to access resources
* @param id The resource id of the animation to load
* @return The animation object reference by the specified id
@@ -268,7 +270,7 @@
XmlResourceParser parser = null;
try {
parser = context.getResources().getAnimation(id);
- return createInterpolatorFromXml(context, parser);
+ return createInterpolatorFromXml(context.getResources(), context.getTheme(), parser);
} catch (XmlPullParserException ex) {
NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
Integer.toHexString(id));
@@ -284,54 +286,84 @@
}
}
-
- private static Interpolator createInterpolatorFromXml(Context c, XmlPullParser parser)
+
+ /**
+ * Loads an {@link Interpolator} object from a resource
+ *
+ * @param res The resources
+ * @param id The resource id of the animation to load
+ * @return The interpolator object reference by the specified id
+ * @throws NotFoundException
+ * @hide
+ */
+ public static Interpolator loadInterpolator(Resources res, Theme theme, int id) throws NotFoundException {
+ XmlResourceParser parser = null;
+ try {
+ parser = res.getAnimation(id);
+ return createInterpolatorFromXml(res, theme, parser);
+ } catch (XmlPullParserException ex) {
+ NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
+ Integer.toHexString(id));
+ rnf.initCause(ex);
+ throw rnf;
+ } catch (IOException ex) {
+ NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
+ Integer.toHexString(id));
+ rnf.initCause(ex);
+ throw rnf;
+ } finally {
+ if (parser != null)
+ parser.close();
+ }
+
+ }
+
+ private static Interpolator createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser)
throws XmlPullParserException, IOException {
-
+
Interpolator interpolator = null;
-
+
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
- while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
- && type != XmlPullParser.END_DOCUMENT) {
+ while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
AttributeSet attrs = Xml.asAttributeSet(parser);
-
- String name = parser.getName();
-
-
+
+ String name = parser.getName();
+
if (name.equals("linearInterpolator")) {
- interpolator = new LinearInterpolator(c, attrs);
+ interpolator = new LinearInterpolator();
} else if (name.equals("accelerateInterpolator")) {
- interpolator = new AccelerateInterpolator(c, attrs);
+ interpolator = new AccelerateInterpolator(res, theme, attrs);
} else if (name.equals("decelerateInterpolator")) {
- interpolator = new DecelerateInterpolator(c, attrs);
- } else if (name.equals("accelerateDecelerateInterpolator")) {
- interpolator = new AccelerateDecelerateInterpolator(c, attrs);
- } else if (name.equals("cycleInterpolator")) {
- interpolator = new CycleInterpolator(c, attrs);
+ interpolator = new DecelerateInterpolator(res, theme, attrs);
+ } else if (name.equals("accelerateDecelerateInterpolator")) {
+ interpolator = new AccelerateDecelerateInterpolator();
+ } else if (name.equals("cycleInterpolator")) {
+ interpolator = new CycleInterpolator(res, theme, attrs);
} else if (name.equals("anticipateInterpolator")) {
- interpolator = new AnticipateInterpolator(c, attrs);
+ interpolator = new AnticipateInterpolator(res, theme, attrs);
} else if (name.equals("overshootInterpolator")) {
- interpolator = new OvershootInterpolator(c, attrs);
+ interpolator = new OvershootInterpolator(res, theme, attrs);
} else if (name.equals("anticipateOvershootInterpolator")) {
- interpolator = new AnticipateOvershootInterpolator(c, attrs);
+ interpolator = new AnticipateOvershootInterpolator(res, theme, attrs);
} else if (name.equals("bounceInterpolator")) {
- interpolator = new BounceInterpolator(c, attrs);
+ interpolator = new BounceInterpolator();
} else if (name.equals("pathInterpolator")) {
- interpolator = new PathInterpolator(c, attrs);
+ interpolator = new PathInterpolator(res, theme, attrs);
} else {
throw new RuntimeException("Unknown interpolator name: " + parser.getName());
}
}
-
+
return interpolator;
}
diff --git a/core/java/android/view/animation/AnticipateInterpolator.java b/core/java/android/view/animation/AnticipateInterpolator.java
index 83a8007..fe756bd 100644
--- a/core/java/android/view/animation/AnticipateInterpolator.java
+++ b/core/java/android/view/animation/AnticipateInterpolator.java
@@ -17,9 +17,12 @@
package android.view.animation;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.util.AttributeSet;
+import com.android.internal.R;
import com.android.internal.view.animation.HasNativeInterpolator;
import com.android.internal.view.animation.NativeInterpolatorFactory;
import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
@@ -45,11 +48,20 @@
}
public AnticipateInterpolator(Context context, AttributeSet attrs) {
- TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.AnticipateInterpolator);
+ this(context.getResources(), context.getTheme(), attrs);
+ }
+
+ /** @hide */
+ public AnticipateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.AnticipateInterpolator, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, R.styleable.AnticipateInterpolator);
+ }
mTension =
- a.getFloat(com.android.internal.R.styleable.AnticipateInterpolator_tension, 2.0f);
+ a.getFloat(R.styleable.AnticipateInterpolator_tension, 2.0f);
a.recycle();
}
diff --git a/core/java/android/view/animation/AnticipateOvershootInterpolator.java b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
index 1a8adfd..78e5acf 100644
--- a/core/java/android/view/animation/AnticipateOvershootInterpolator.java
+++ b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
@@ -17,6 +17,8 @@
package android.view.animation;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.util.AttributeSet;
@@ -62,7 +64,17 @@
}
public AnticipateOvershootInterpolator(Context context, AttributeSet attrs) {
- TypedArray a = context.obtainStyledAttributes(attrs, AnticipateOvershootInterpolator);
+ this(context.getResources(), context.getTheme(), attrs);
+ }
+
+ /** @hide */
+ public AnticipateOvershootInterpolator(Resources res, Theme theme, AttributeSet attrs) {
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, AnticipateOvershootInterpolator, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, AnticipateOvershootInterpolator);
+ }
mTension = a.getFloat(AnticipateOvershootInterpolator_tension, 2.0f) *
a.getFloat(AnticipateOvershootInterpolator_extraTension, 1.5f);
diff --git a/core/java/android/view/animation/CycleInterpolator.java b/core/java/android/view/animation/CycleInterpolator.java
index d1ebf05..3114aa3 100644
--- a/core/java/android/view/animation/CycleInterpolator.java
+++ b/core/java/android/view/animation/CycleInterpolator.java
@@ -17,9 +17,12 @@
package android.view.animation;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.util.AttributeSet;
+import com.android.internal.R;
import com.android.internal.view.animation.HasNativeInterpolator;
import com.android.internal.view.animation.NativeInterpolatorFactory;
import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
@@ -34,20 +37,29 @@
public CycleInterpolator(float cycles) {
mCycles = cycles;
}
-
+
public CycleInterpolator(Context context, AttributeSet attrs) {
- TypedArray a =
- context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.CycleInterpolator);
-
- mCycles = a.getFloat(com.android.internal.R.styleable.CycleInterpolator_cycles, 1.0f);
-
+ this(context.getResources(), context.getTheme(), attrs);
+ }
+
+ /** @hide */
+ public CycleInterpolator(Resources resources, Theme theme, AttributeSet attrs) {
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.CycleInterpolator, 0, 0);
+ } else {
+ a = resources.obtainAttributes(attrs, R.styleable.CycleInterpolator);
+ }
+
+ mCycles = a.getFloat(R.styleable.CycleInterpolator_cycles, 1.0f);
+
a.recycle();
}
-
+
public float getInterpolation(float input) {
return (float)(Math.sin(2 * mCycles * Math.PI * input));
}
-
+
private float mCycles;
/** @hide */
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 0789a0e..674207c 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -17,15 +17,18 @@
package android.view.animation;
import android.content.Context;
+import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.content.res.Resources.Theme;
import android.util.AttributeSet;
+import com.android.internal.R;
import com.android.internal.view.animation.HasNativeInterpolator;
import com.android.internal.view.animation.NativeInterpolatorFactory;
import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
/**
- * An interpolator where the rate of change starts out quickly and
+ * An interpolator where the rate of change starts out quickly and
* and then decelerates.
*
*/
@@ -36,7 +39,7 @@
/**
* Constructor
- *
+ *
* @param factor Degree to which the animation should be eased. Setting factor to 1.0f produces
* an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the
* ease-out effect (i.e., it starts even faster and ends evens slower)
@@ -44,16 +47,25 @@
public DecelerateInterpolator(float factor) {
mFactor = factor;
}
-
+
public DecelerateInterpolator(Context context, AttributeSet attrs) {
- TypedArray a =
- context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.DecelerateInterpolator);
-
- mFactor = a.getFloat(com.android.internal.R.styleable.DecelerateInterpolator_factor, 1.0f);
-
+ this(context.getResources(), context.getTheme(), attrs);
+ }
+
+ /** @hide */
+ public DecelerateInterpolator(Resources res, Theme theme, AttributeSet attrs) {
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.DecelerateInterpolator, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, R.styleable.DecelerateInterpolator);
+ }
+
+ mFactor = a.getFloat(R.styleable.DecelerateInterpolator_factor, 1.0f);
+
a.recycle();
}
-
+
public float getInterpolation(float input) {
float result;
if (mFactor == 1.0f) {
@@ -63,7 +75,7 @@
}
return result;
}
-
+
private float mFactor = 1.0f;
/** @hide */
diff --git a/core/java/android/view/animation/OvershootInterpolator.java b/core/java/android/view/animation/OvershootInterpolator.java
index a2466f1..d6c2808 100644
--- a/core/java/android/view/animation/OvershootInterpolator.java
+++ b/core/java/android/view/animation/OvershootInterpolator.java
@@ -17,9 +17,12 @@
package android.view.animation;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.util.AttributeSet;
+import com.android.internal.R;
import com.android.internal.view.animation.HasNativeInterpolator;
import com.android.internal.view.animation.NativeInterpolatorFactory;
import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
@@ -46,11 +49,20 @@
}
public OvershootInterpolator(Context context, AttributeSet attrs) {
- TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.OvershootInterpolator);
+ this(context.getResources(), context.getTheme(), attrs);
+ }
+
+ /** @hide */
+ public OvershootInterpolator(Resources res, Theme theme, AttributeSet attrs) {
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.OvershootInterpolator, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, R.styleable.OvershootInterpolator);
+ }
mTension =
- a.getFloat(com.android.internal.R.styleable.OvershootInterpolator_tension, 2.0f);
+ a.getFloat(R.styleable.OvershootInterpolator_tension, 2.0f);
a.recycle();
}
diff --git a/core/java/android/view/animation/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java
index a369509..da12ffb 100644
--- a/core/java/android/view/animation/PathInterpolator.java
+++ b/core/java/android/view/animation/PathInterpolator.java
@@ -16,11 +16,15 @@
package android.view.animation;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.InflateException;
+import com.android.internal.R;
+
/**
* An interpolator that can traverse a Path that extends from <code>Point</code>
* <code>(0, 0)</code> to <code>(1, 1)</code>. The x coordinate along the <code>Path</code>
@@ -81,18 +85,33 @@
}
public PathInterpolator(Context context, AttributeSet attrs) {
- TypedArray a = context.obtainStyledAttributes(attrs,
- com.android.internal.R.styleable.PathInterpolator);
- if (!a.hasValue(com.android.internal.R.styleable.PathInterpolator_controlX1)) {
+ this(context.getResources(), context.getTheme(), attrs);
+ }
+
+ /** @hide */
+ public PathInterpolator(Resources res, Theme theme, AttributeSet attrs) {
+ TypedArray a;
+ if (theme != null) {
+ a = theme.obtainStyledAttributes(attrs, R.styleable.PathInterpolator, 0, 0);
+ } else {
+ a = res.obtainAttributes(attrs, R.styleable.PathInterpolator);
+ }
+ parseInterpolatorFromTypeArray(a);
+
+ a.recycle();
+ }
+
+ private void parseInterpolatorFromTypeArray(TypedArray a) {
+ if (!a.hasValue(R.styleable.PathInterpolator_controlX1)) {
throw new InflateException("pathInterpolator requires the controlX1 attribute");
- } else if (!a.hasValue(com.android.internal.R.styleable.PathInterpolator_controlY1)) {
+ } else if (!a.hasValue(R.styleable.PathInterpolator_controlY1)) {
throw new InflateException("pathInterpolator requires the controlY1 attribute");
}
- float x1 = a.getFloat(com.android.internal.R.styleable.PathInterpolator_controlX1, 0);
- float y1 = a.getFloat(com.android.internal.R.styleable.PathInterpolator_controlY1, 0);
+ float x1 = a.getFloat(R.styleable.PathInterpolator_controlX1, 0);
+ float y1 = a.getFloat(R.styleable.PathInterpolator_controlY1, 0);
- boolean hasX2 = a.hasValue(com.android.internal.R.styleable.PathInterpolator_controlX2);
- boolean hasY2 = a.hasValue(com.android.internal.R.styleable.PathInterpolator_controlY2);
+ boolean hasX2 = a.hasValue(R.styleable.PathInterpolator_controlX2);
+ boolean hasY2 = a.hasValue(R.styleable.PathInterpolator_controlY2);
if (hasX2 != hasY2) {
throw new InflateException(
@@ -102,12 +121,10 @@
if (!hasX2) {
initQuad(x1, y1);
} else {
- float x2 = a.getFloat(com.android.internal.R.styleable.PathInterpolator_controlX2, 0);
- float y2 = a.getFloat(com.android.internal.R.styleable.PathInterpolator_controlY2, 0);
+ float x2 = a.getFloat(R.styleable.PathInterpolator_controlX2, 0);
+ float y2 = a.getFloat(R.styleable.PathInterpolator_controlY2, 0);
initCubic(x1, y1, x2, y2);
}
-
- a.recycle();
}
private void initQuad(float controlX, float controlY) {
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index cccfa78..e1f40b7 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -195,6 +195,7 @@
public boolean commitText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "commitText " + text);
replaceText(text, newCursorPosition, false);
+ mIMM.notifyUserAction();
sendCurrentText();
return true;
}
@@ -435,6 +436,7 @@
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "setComposingText " + text);
replaceText(text, newCursorPosition, true);
+ mIMM.notifyUserAction();
return true;
}
@@ -518,6 +520,7 @@
viewRootImpl.dispatchKeyFromIme(event);
}
}
+ mIMM.notifyUserAction();
return false;
}
@@ -601,10 +604,6 @@
}
beginBatchEdit();
- if (!composing && !TextUtils.isEmpty(text)) {
- // Notify the text is committed by the user to InputMethodManagerService
- mIMM.notifyTextCommitted();
- }
// delete composing text set previously.
int a = getComposingSpanStart(content);
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
index fad6747..66f5f6c 100644
--- a/core/java/android/view/inputmethod/CursorAnchorInfo.java
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -186,12 +186,12 @@
/**
* Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe.
*/
- public static final class CursorAnchorInfoBuilder {
+ public static final class Builder {
/**
* Sets the text range of the selection. Calling this can be skipped if there is no
* selection.
*/
- public CursorAnchorInfoBuilder setSelectionRange(final int newStart, final int newEnd) {
+ public Builder setSelectionRange(final int newStart, final int newEnd) {
mSelectionStart = newStart;
mSelectionEnd = newEnd;
return this;
@@ -205,8 +205,7 @@
* @param index index where the composing text starts.
* @param composingText the entire composing text.
*/
- public CursorAnchorInfoBuilder setComposingText(final int index,
- final CharSequence composingText) {
+ public Builder setComposingText(final int index, final CharSequence composingText) {
mComposingTextStart = index;
if (composingText == null) {
mComposingText = null;
@@ -236,9 +235,8 @@
* that will be transformed with the transformation matrix when rendered on the screen. This
* should be calculated or compatible with {@link Layout#getLineBottom(int)}.
*/
- public CursorAnchorInfoBuilder setInsertionMarkerLocation(
- final float horizontalPosition, final float lineTop, final float lineBaseline,
- final float lineBottom){
+ public Builder setInsertionMarkerLocation(final float horizontalPosition,
+ final float lineTop, final float lineBaseline, final float lineBottom){
mInsertionMarkerHorizontal = horizontalPosition;
mInsertionMarkerTop = lineTop;
mInsertionMarkerBaseline = lineBaseline;
@@ -269,9 +267,8 @@
* @throws IllegalArgumentException If the index is a negative value, or not greater than
* all of the previously called indices.
*/
- public CursorAnchorInfoBuilder addCharacterRect(final int index,
- final float leadingEdgeX, final float leadingEdgeY, final float trailingEdgeX,
- final float trailingEdgeY) {
+ public Builder addCharacterRect(final int index, final float leadingEdgeX,
+ final float leadingEdgeY, final float trailingEdgeX, final float trailingEdgeY) {
if (index < 0) {
throw new IllegalArgumentException("index must not be a negative integer.");
}
@@ -289,7 +286,7 @@
* @param matrix transformation matrix from local coordinates into screen coordinates. null
* is interpreted as an identity matrix.
*/
- public CursorAnchorInfoBuilder setMatrix(final Matrix matrix) {
+ public Builder setMatrix(final Matrix matrix) {
mMatrix.set(matrix != null ? matrix : Matrix.IDENTITY_MATRIX);
return this;
}
@@ -297,7 +294,7 @@
/**
* @return {@link CursorAnchorInfo} using parameters in this
- * {@link CursorAnchorInfoBuilder}.
+ * {@link Builder}.
*/
public CursorAnchorInfo build() {
return new CursorAnchorInfo(this);
@@ -323,7 +320,7 @@
}
}
- private CursorAnchorInfo(final CursorAnchorInfoBuilder builder) {
+ private CursorAnchorInfo(final Builder builder) {
mSelectionStart = builder.mSelectionStart;
mSelectionEnd = builder.mSelectionEnd;
mComposingTextStart = builder.mComposingTextStart;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f874eb7..ace8808 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -320,6 +320,25 @@
int mCursorCandEnd;
/**
+ * Represents an invalid action notification sequence number. {@link InputMethodManagerService}
+ * always issues a positive integer for action notification sequence numbers. Thus -1 is
+ * guaranteed to be different from any valid sequence number.
+ */
+ private static final int NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER = -1;
+ /**
+ * The next sequence number that is to be sent to {@link InputMethodManagerService} via
+ * {@link IInputMethodManager#notifyUserAction(int)} at once when a user action is observed.
+ */
+ private int mNextUserActionNotificationSequenceNumber =
+ NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
+
+ /**
+ * The last sequence number that is already sent to {@link InputMethodManagerService}.
+ */
+ private int mLastSentUserActionNotificationSequenceNumber =
+ NOT_AN_ACTION_NOTIFICATION_SEQUENCE_NUMBER;
+
+ /**
* The instance that has previously been sent to the input method.
*/
private CursorAnchorInfo mCursorAnchorInfo = null;
@@ -363,7 +382,8 @@
static final int MSG_SEND_INPUT_EVENT = 5;
static final int MSG_TIMEOUT_INPUT_EVENT = 6;
static final int MSG_FLUSH_INPUT_EVENT = 7;
- static final int SET_CURSOR_ANCHOR_MONITOR_MODE = 8;
+ static final int MSG_SET_CURSOR_ANCHOR_MONITOR_MODE = 8;
+ static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 9;
class H extends Handler {
H(Looper looper) {
@@ -494,7 +514,7 @@
finishedInputEvent(msg.arg1, false, false);
return;
}
- case SET_CURSOR_ANCHOR_MONITOR_MODE: {
+ case MSG_SET_CURSOR_ANCHOR_MONITOR_MODE: {
synchronized (mH) {
mCursorAnchorMonitorMode = msg.arg1;
// Clear the cache.
@@ -503,6 +523,11 @@
}
return;
}
+ case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
+ synchronized (mH) {
+ mNextUserActionNotificationSequenceNumber = msg.arg1;
+ }
+ }
}
}
}
@@ -570,7 +595,13 @@
@Override
public void setCursorAnchorMonitorMode(int monitorMode) {
- mH.sendMessage(mH.obtainMessage(SET_CURSOR_ANCHOR_MONITOR_MODE, monitorMode, 0));
+ mH.sendMessage(mH.obtainMessage(MSG_SET_CURSOR_ANCHOR_MONITOR_MODE, monitorMode, 0));
+ }
+
+ @Override
+ public void setUserActionNotificationSequenceNumber(int sequenceNumber) {
+ mH.sendMessage(mH.obtainMessage(MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
+ sequenceNumber, 0));
}
};
@@ -1214,6 +1245,8 @@
mBindSequence = res.sequence;
mCurMethod = res.method;
mCurId = res.id;
+ mNextUserActionNotificationSequenceNumber =
+ res.userActionNotificationSequenceNumber;
} else {
if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
@@ -1913,13 +1946,33 @@
}
/**
- * Notify the current IME commits text
+ * Notify that a user took some action with this input method.
* @hide
*/
- public void notifyTextCommitted() {
+ public void notifyUserAction() {
synchronized (mH) {
+ if (mLastSentUserActionNotificationSequenceNumber ==
+ mNextUserActionNotificationSequenceNumber) {
+ if (DEBUG) {
+ Log.w(TAG, "Ignoring notifyUserAction as it has already been sent."
+ + " mLastSentUserActionNotificationSequenceNumber: "
+ + mLastSentUserActionNotificationSequenceNumber
+ + " mNextUserActionNotificationSequenceNumber: "
+ + mNextUserActionNotificationSequenceNumber);
+ }
+ return;
+ }
try {
- mService.notifyTextCommitted();
+ if (DEBUG) {
+ Log.w(TAG, "notifyUserAction: "
+ + " mLastSentUserActionNotificationSequenceNumber: "
+ + mLastSentUserActionNotificationSequenceNumber
+ + " mNextUserActionNotificationSequenceNumber: "
+ + mNextUserActionNotificationSequenceNumber);
+ }
+ mService.notifyUserAction(mNextUserActionNotificationSequenceNumber);
+ mLastSentUserActionNotificationSequenceNumber =
+ mNextUserActionNotificationSequenceNumber;
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
@@ -2103,6 +2156,10 @@
+ " mCursorSelEnd=" + mCursorSelEnd
+ " mCursorCandStart=" + mCursorCandStart
+ " mCursorCandEnd=" + mCursorCandEnd);
+ p.println(" mNextUserActionNotificationSequenceNumber="
+ + mNextUserActionNotificationSequenceNumber
+ + " mLastSentUserActionNotificationSequenceNumber="
+ + mLastSentUserActionNotificationSequenceNumber);
}
/**
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 2b75d83..abed082 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -70,8 +70,7 @@
/**
* Sets a cookie for the given URL. Any existing cookie with the same host,
* path and name will be replaced with the new cookie. The cookie being set
- * must not have expired and must not be a session cookie, otherwise it
- * will be ignored.
+ * will be ignored if it is expired.
*
* @param url the URL for which the cookie is set
* @param value the cookie as a string, using the format of the 'Set-Cookie'
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index d630a9a..ec396aa 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -92,7 +92,7 @@
@Deprecated
public void onShowCustomView(View view, int requestedOrientation,
CustomViewCallback callback) {};
-
+
/**
* Notify the host application that the current page would
* like to hide its custom view.
@@ -392,6 +392,79 @@
}
/**
+ * Tell the client to show a file chooser.
+ *
+ * This is called to handle HTML forms with 'file' input type, in response to the
+ * user pressing the "Select File" button.
+ * To cancel the request, call <code>filePathCallback.onReceiveValue(null)</code> and
+ * return true.
+ *
+ * @param webView The WebView instance that is initiating the request.
+ * @param filePathCallback Invoke this callback to supply the list of paths to files to upload,
+ * or NULL to cancel. Must only be called if the
+ * <code>showFileChooser</code> implementations returns true.
+ * @param fileChooserParams Describes the mode of file chooser to be opened, and options to be
+ * used with it.
+ * @return true if filePathCallback will be invoked, false to use default handling.
+ *
+ * @see FileChooserParams
+ * @hide For API approval
+ */
+ public boolean showFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
+ FileChooserParams fileChooserParams) {
+ return false;
+ }
+
+ /**
+ * Parameters used in the {@link #showFileChooser(WebView,ValueCallback<String[]>,FileChooserParams)}
+ * method.
+ *
+ * This is intended to be used as a read-only data struct by the application.
+ * @hide For API approval
+ */
+ public static class FileChooserParams {
+ // Flags for mode
+ /** Bitflag for <code>mode</code> indicating multiple files maybe selected */
+ public static final int MODE_OPEN_MULTIPLE = 1 << 0;
+ /** Bitflag for <code>mode</code> indicating a folder maybe selected.
+ * The implementation should enumerate all files selected by this operation */
+ public static final int MODE_OPEN_FOLDER = 1 << 1;
+ /** Bitflag for <code>mode</code> indicating a non-existant filename maybe returned */
+ public static final int MODE_SAVE = 1 << 2;
+
+ /**
+ * Bit-field of the <code>MODE_</code> flags.
+ *
+ * 0 indicates plain single file open.
+ */
+ public int mode;
+
+ /**
+ * Comma-seperated list of acceptable MIME types.
+ */
+ public String acceptTypes;
+
+ /**
+ * true indicates a preference for a live media captured value (e.g. Camera, Microphone).
+ *
+ * Use <code>acceptTypes</code> to determine suitable capture devices.
+ */
+ public boolean capture;
+
+ /**
+ * The title to use for this file selector, or null.
+ *
+ * Maybe null, in which case a default title should be used.
+ */
+ public String title;
+
+ /**
+ * Name of a default selection if appropriate, or null.
+ */
+ public String defaultFilename;
+ };
+
+ /**
* Tell the client to open a file chooser.
* @param uploadFile A ValueCallback to set the URI of the file to upload.
* onReceiveValue must be called to wake up the thread.a
@@ -399,8 +472,11 @@
* associated with this file picker.
* @param capture The value of the 'capture' attribute of the input tag
* associated with this file picker.
- * @hide
+ *
+ * @deprecated Use {@link #showFileChooser} instead.
+ * @hide This method was not published in any SDK version.
*/
+ @Deprecated
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
uploadFile.onReceiveValue(null);
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 7c32c5b..d14c19b 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1460,4 +1460,36 @@
* {@link #MIXED_CONTENT_NEVER_ALLOW} or {@link #MIXED_CONTENT_COMPATIBILITY_MODE}.
*/
public abstract int getMixedContentMode();
+
+ /**
+ * Sets whether to use a video overlay for embedded encrypted video.
+ * In API levels prior to {@link android.os.Build.VERSION_CODES#L}, encrypted video can
+ * only be rendered directly on a secure video surface, so it had been a hard problem to play
+ * encrypted video in HTML. When this flag is on, WebView can play encrypted video (MSE/EME)
+ * by using a video overlay (aka hole-punching) for videos embedded using HTML <video>
+ * tag.<br>
+ * Caution: This setting is intended for use only in a narrow set of circumstances and apps
+ * should only enable it if they require playback of encrypted video content. It will impose
+ * the following limitations on the WebView:
+ * <ul>
+ * <li> Only one video overlay can be played at a time.
+ * <li> Changes made to position or dimensions of a video element may be propagated to the
+ * corresponding video overlay with a noticeable delay.
+ * <li> The video overlay is not visible to web APIs and as such may not interact with
+ * script or styling. For example, CSS styles applied to the <video> tag may be ignored.
+ * </ul>
+ * This is not an exhaustive set of constraints and it may vary with new versions of the
+ * WebView.
+ * @hide
+ */
+ public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag);
+
+ /**
+ * Gets whether a video overlay will be used for embedded encrypted video.
+ *
+ * @return true if WebView uses a video overlay for embedded encrypted video.
+ * @see #setVideoOverlayForEmbeddedEncryptedVideoEnabled
+ * @hide
+ */
+ public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled();
}
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 945e0e3..6e6a987 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -37,13 +37,6 @@
String findAddress(String addr);
/**
- * Implements the API methods:
- * {@link android.webkit.WebView#enablePlatformNotifications()}
- * {@link android.webkit.WebView#disablePlatformNotifications()}
- */
- void setPlatformNotificationsEnabled(boolean enable);
-
- /**
* Implements the API method:
* {@link android.webkit.WebSettings#getDefaultUserAgent(Context) }
*/
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 9a46052..372228c 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4028,12 +4028,10 @@
final int scrollY = mScrollY;
if (!mEdgeGlowTop.isFinished()) {
final int restoreCount = canvas.save();
- final int leftPadding = mListPadding.left + mGlowPaddingLeft;
- final int rightPadding = mListPadding.right + mGlowPaddingRight;
- final int width = getWidth() - leftPadding - rightPadding;
+ final int width = getWidth();
int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess);
- canvas.translate(leftPadding, edgeY);
+ canvas.translate(0, edgeY);
mEdgeGlowTop.setSize(width, getHeight());
if (mEdgeGlowTop.draw(canvas)) {
invalidate(0, 0, getWidth(),
@@ -4043,12 +4041,10 @@
}
if (!mEdgeGlowBottom.isFinished()) {
final int restoreCount = canvas.save();
- final int leftPadding = mListPadding.left + mGlowPaddingLeft;
- final int rightPadding = mListPadding.right + mGlowPaddingRight;
- final int width = getWidth() - leftPadding - rightPadding;
+ final int width = getWidth();
final int height = getHeight();
- int edgeX = -width + leftPadding;
+ int edgeX = -width;
int edgeY = Math.max(height, scrollY + mLastPositionDistanceGuess);
canvas.translate(edgeX, edgeY);
canvas.rotate(180, width, 0);
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index c4a40b4..2502954 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -122,7 +122,7 @@
final TypedArray a = context.obtainStyledAttributes(
com.android.internal.R.styleable.EdgeEffect);
final int themeColor = a.getColor(
- com.android.internal.R.styleable.EdgeEffect_colorPrimaryLight, 0xff666666);
+ com.android.internal.R.styleable.EdgeEffect_colorPrimary, 0xff666666);
a.recycle();
mPaint.setColor((themeColor & 0xffffff) | 0x33000000);
mPaint.setStyle(Paint.Style.FILL);
@@ -312,8 +312,7 @@
final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
float translateX = mBounds.width() * displacement / 2;
- canvas.clipRect(Float.MIN_VALUE, mBounds.top,
- Float.MAX_VALUE, Float.MAX_VALUE);
+ canvas.clipRect(mBounds);
canvas.translate(translateX, 0);
canvas.drawArc(mArcRect, 45, 90, true, mPaint);
canvas.restoreToCount(count);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 27d6b82..4467128 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -96,7 +96,6 @@
import android.view.WindowManager;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -3013,7 +3012,7 @@
* {@link InputMethodManager#isWatchingCursor(View)} returns false.
*/
private final class CursorAnchorInfoNotifier implements TextViewPositionListener {
- final CursorAnchorInfoBuilder mSelectionInfoBuilder = new CursorAnchorInfoBuilder();
+ final CursorAnchorInfo.Builder mSelectionInfoBuilder = new CursorAnchorInfo.Builder();
final int[] mTmpIntOffset = new int[2];
final Matrix mViewToScreenMatrix = new Matrix();
@@ -3037,7 +3036,7 @@
return;
}
- final CursorAnchorInfoBuilder builder = mSelectionInfoBuilder;
+ final CursorAnchorInfo.Builder builder = mSelectionInfoBuilder;
builder.reset();
final int selectionStart = mTextView.getSelectionStart();
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index eedacb5..572302a4 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -664,7 +664,7 @@
InputStream stream = null;
try {
stream = mContext.getContentResolver().openInputStream(mUri);
- d = Drawable.createFromStreamThemed(stream, null, mContext.getTheme());
+ d = Drawable.createFromStream(stream, null);
} catch (Exception e) {
Log.w("ImageView", "Unable to open content: " + mUri, e);
} finally {
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 0c3715d..b49938c 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -357,9 +357,8 @@
Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
shapeDrawable.getPaint().setShader(bitmapShader);
- // Ensure the color filter and tint are propagated.
- shapeDrawable.setTint(bitmap.getTint());
- shapeDrawable.setTintMode(bitmap.getTintMode());
+ // Ensure the tint and filter are propagated in the correct order.
+ shapeDrawable.setTint(bitmap.getTint(), bitmap.getTintMode());
shapeDrawable.setColorFilter(bitmap.getColorFilter());
return clip ? new ClipDrawable(
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 1c9ab61..883183e 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -42,6 +42,7 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+
import com.android.internal.R;
import java.text.DateFormatSymbols;
@@ -82,13 +83,13 @@
private static final int ALPHA_TRANSPARENT = 0;
// Alpha level of color for selector.
- private static final int ALPHA_SELECTOR = 51;
+ private static final int ALPHA_SELECTOR = 255; // was 51
// Alpha level of color for selected circle.
private static final int ALPHA_AMPM_SELECTED = ALPHA_SELECTOR;
// Alpha level of color for pressed circle.
- private static final int ALPHA_AMPM_PRESSED = 175;
+ private static final int ALPHA_AMPM_PRESSED = 255; // was 175
private static final float COSINE_30_DEGREES = ((float) Math.sqrt(3)) * 0.5f;
private static final float SINE_30_DEGREES = 0.5f;
@@ -112,8 +113,15 @@
private final String[] mAmPmText = new String[2];
private final Paint[] mPaint = new Paint[2];
+ private final int[] mColor = new int[2];
+ private final IntHolder[] mAlpha = new IntHolder[2];
+
private final Paint mPaintCenter = new Paint();
+
private final Paint[][] mPaintSelector = new Paint[2][3];
+ private final int[][] mColorSelector = new int[2][3];
+ private final IntHolder[][] mAlphaSelector = new IntHolder[2][3];
+
private final Paint mPaintAmPmText = new Paint();
private final Paint[] mPaintAmPmCircle = new Paint[2];
@@ -309,65 +317,80 @@
final Resources res = getResources();
mAmPmUnselectedColor = a.getColor(R.styleable.TimePicker_amPmUnselectedBackgroundColor,
- res.getColor(
- R.color.timepicker_default_ampm_unselected_background_color_holo_light));
+ res.getColor(R.color.timepicker_default_ampm_unselected_background_color_material));
mAmPmSelectedColor = a.getColor(R.styleable.TimePicker_amPmSelectedBackgroundColor,
- res.getColor(R.color.timepicker_default_ampm_selected_background_color_holo_light));
+ res.getColor(R.color.timepicker_default_ampm_selected_background_color_material));
mAmPmTextColor = a.getColor(R.styleable.TimePicker_amPmTextColor,
- res.getColor(R.color.timepicker_default_text_color_holo_light));
-
- final int numbersTextColor = a.getColor(R.styleable.TimePicker_numbersTextColor,
- res.getColor(R.color.timepicker_default_text_color_holo_light));
+ res.getColor(R.color.timepicker_default_text_color_material));
mTypeface = Typeface.create("sans-serif", Typeface.NORMAL);
+ // Initialize all alpha values to opaque.
+ for (int i = 0; i < mAlpha.length; i++) {
+ mAlpha[i] = new IntHolder(ALPHA_OPAQUE);
+ }
+ for (int i = 0; i < mAlphaSelector.length; i++) {
+ for (int j = 0; j < mAlphaSelector[i].length; j++) {
+ mAlphaSelector[i][j] = new IntHolder(ALPHA_OPAQUE);
+ }
+ }
+
+ final int numbersTextColor = a.getColor(R.styleable.TimePicker_numbersTextColor,
+ res.getColor(R.color.timepicker_default_text_color_material));
+
mPaint[HOURS] = new Paint();
- mPaint[HOURS].setColor(numbersTextColor);
mPaint[HOURS].setAntiAlias(true);
mPaint[HOURS].setTextAlign(Paint.Align.CENTER);
+ mColor[HOURS] = numbersTextColor;
mPaint[MINUTES] = new Paint();
- mPaint[MINUTES].setColor(numbersTextColor);
mPaint[MINUTES].setAntiAlias(true);
mPaint[MINUTES].setTextAlign(Paint.Align.CENTER);
+ mColor[MINUTES] = numbersTextColor;
mPaintCenter.setColor(numbersTextColor);
mPaintCenter.setAntiAlias(true);
mPaintCenter.setTextAlign(Paint.Align.CENTER);
mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint();
- mPaintSelector[HOURS][SELECTOR_CIRCLE].setColor(
- a.getColor(R.styleable.TimePicker_numbersSelectorColor, R.color.holo_blue_light));
mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true);
+ mColorSelector[HOURS][SELECTOR_CIRCLE] = a.getColor(
+ R.styleable.TimePicker_numbersSelectorColor,
+ R.color.timepicker_default_selector_color_material);
mPaintSelector[HOURS][SELECTOR_DOT] = new Paint();
- mPaintSelector[HOURS][SELECTOR_DOT].setColor(
- a.getColor(R.styleable.TimePicker_numbersSelectorColor, R.color.holo_blue_light));
mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true);
+ mColorSelector[HOURS][SELECTOR_DOT] = a.getColor(
+ R.styleable.TimePicker_numbersSelectorColor,
+ R.color.timepicker_default_selector_color_material);
mPaintSelector[HOURS][SELECTOR_LINE] = new Paint();
- mPaintSelector[HOURS][SELECTOR_LINE].setColor(
- a.getColor(R.styleable.TimePicker_numbersSelectorColor, R.color.holo_blue_light));
mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true);
mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2);
+ mColorSelector[HOURS][SELECTOR_LINE] = a.getColor(
+ R.styleable.TimePicker_numbersSelectorColor,
+ R.color.timepicker_default_selector_color_material);
mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_CIRCLE].setColor(
- a.getColor(R.styleable.TimePicker_numbersSelectorColor, R.color.holo_blue_light));
mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true);
+ mColorSelector[MINUTES][SELECTOR_CIRCLE] = a.getColor(
+ R.styleable.TimePicker_numbersSelectorColor,
+ R.color.timepicker_default_selector_color_material);
mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_DOT].setColor(
- a.getColor(R.styleable.TimePicker_numbersSelectorColor, R.color.holo_blue_light));
mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true);
+ mColorSelector[MINUTES][SELECTOR_DOT] = a.getColor(
+ R.styleable.TimePicker_numbersSelectorColor,
+ R.color.timepicker_default_selector_color_material);
mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_LINE].setColor(
- a.getColor(R.styleable.TimePicker_numbersSelectorColor, R.color.holo_blue_light));
mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true);
mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2);
+ mColorSelector[MINUTES][SELECTOR_LINE] = a.getColor(
+ R.styleable.TimePicker_numbersSelectorColor,
+ R.color.timepicker_default_selector_color_material);
mPaintAmPmText.setColor(mAmPmTextColor);
mPaintAmPmText.setTypeface(mTypeface);
@@ -379,13 +402,12 @@
mPaintAmPmCircle[PM] = new Paint();
mPaintAmPmCircle[PM].setAntiAlias(true);
- mPaintBackground.setColor(
- a.getColor(R.styleable.TimePicker_numbersBackgroundColor, Color.WHITE));
+ mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
+ res.getColor(R.color.timepicker_default_numbers_background_color_material)));
mPaintBackground.setAntiAlias(true);
- final int disabledColor = a.getColor(R.styleable.TimePicker_disabledColor,
- res.getColor(R.color.timepicker_default_disabled_color_holo_light));
- mPaintDisabled.setColor(disabledColor);
+ mPaintDisabled.setColor(a.getColor(R.styleable.TimePicker_disabledColor,
+ res.getColor(R.color.timepicker_default_disabled_color_material)));
mPaintDisabled.setAntiAlias(true);
if (DEBUG) {
@@ -415,6 +437,8 @@
mSelectionRadiusMultiplier = Float.parseFloat(
res.getString(R.string.timepicker_selection_radius_multiplier));
+ a.recycle();
+
setOnTouchListener(this);
// Initial values
@@ -622,21 +646,21 @@
mAmPmCircleRadiusMultiplier = Float.parseFloat(
res.getString(R.string.timepicker_ampm_circle_radius_multiplier));
- mPaint[HOURS].setAlpha(mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT);
- mPaint[MINUTES].setAlpha(mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE);
+ mAlpha[HOURS].setValue(mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT);
+ mAlpha[MINUTES].setValue(mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE);
- mPaintSelector[HOURS][SELECTOR_CIRCLE].setAlpha(
- mShowHours ?ALPHA_SELECTOR : ALPHA_TRANSPARENT);
- mPaintSelector[HOURS][SELECTOR_DOT].setAlpha(
+ mAlphaSelector[HOURS][SELECTOR_CIRCLE].setValue(
+ mShowHours ? ALPHA_SELECTOR : ALPHA_TRANSPARENT);
+ mAlphaSelector[HOURS][SELECTOR_DOT].setValue(
mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT);
- mPaintSelector[HOURS][SELECTOR_LINE].setAlpha(
+ mAlphaSelector[HOURS][SELECTOR_LINE].setValue(
mShowHours ? ALPHA_SELECTOR : ALPHA_TRANSPARENT);
- mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAlpha(
+ mAlphaSelector[MINUTES][SELECTOR_CIRCLE].setValue(
mShowHours ? ALPHA_TRANSPARENT : ALPHA_SELECTOR);
- mPaintSelector[MINUTES][SELECTOR_DOT].setAlpha(
+ mAlphaSelector[MINUTES][SELECTOR_DOT].setValue(
mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE);
- mPaintSelector[MINUTES][SELECTOR_LINE].setAlpha(
+ mAlphaSelector[MINUTES][SELECTOR_LINE].setValue(
mShowHours ? ALPHA_TRANSPARENT : ALPHA_SELECTOR);
}
@@ -704,20 +728,23 @@
calculateGridSizesMinutes();
drawCircleBackground(canvas);
+ drawSelector(canvas);
drawTextElements(canvas, mTextSize[HOURS], mTypeface, mOuterTextHours,
- mTextGridWidths[HOURS], mTextGridHeights[HOURS], mPaint[HOURS]);
+ mTextGridWidths[HOURS], mTextGridHeights[HOURS], mPaint[HOURS],
+ mColor[HOURS], mAlpha[HOURS].getValue());
if (mIs24HourMode && mInnerTextHours != null) {
drawTextElements(canvas, mInnerTextSize, mTypeface, mInnerTextHours,
- mInnerTextGridWidths, mInnerTextGridHeights, mPaint[HOURS]);
+ mInnerTextGridWidths, mInnerTextGridHeights, mPaint[HOURS],
+ mColor[HOURS], mAlpha[HOURS].getValue());
}
drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mOuterTextMinutes,
- mTextGridWidths[MINUTES], mTextGridHeights[MINUTES], mPaint[MINUTES]);
+ mTextGridWidths[MINUTES], mTextGridHeights[MINUTES], mPaint[MINUTES],
+ mColor[MINUTES], mAlpha[MINUTES].getValue());
drawCenter(canvas);
- drawSelector(canvas);
if (!mIs24HourMode) {
drawAmPm(canvas);
}
@@ -772,12 +799,12 @@
// Draw the two circles
mPaintAmPmCircle[AM].setColor(amColor);
- mPaintAmPmCircle[AM].setAlpha(amAlpha);
+ mPaintAmPmCircle[AM].setAlpha(getMultipliedAlpha(amColor, amAlpha));
canvas.drawCircle(isLayoutRtl ? mRightIndicatorXCenter : mLeftIndicatorXCenter,
mAmPmYCenter, mAmPmCircleRadius, mPaintAmPmCircle[AM]);
mPaintAmPmCircle[PM].setColor(pmColor);
- mPaintAmPmCircle[PM].setAlpha(pmAlpha);
+ mPaintAmPmCircle[PM].setAlpha(getMultipliedAlpha(pmColor, pmAlpha));
canvas.drawCircle(isLayoutRtl ? mLeftIndicatorXCenter : mRightIndicatorXCenter,
mAmPmYCenter, mAmPmCircleRadius, mPaintAmPmCircle[PM]);
@@ -792,6 +819,10 @@
textYCenter, mPaintAmPmText);
}
+ private int getMultipliedAlpha(int argb, int alpha) {
+ return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5);
+ }
+
private void drawSelector(Canvas canvas, int index) {
// Calculate the current radius at which to place the selection circle.
mLineLength[index] = (int) (mCircleRadius[index]
@@ -802,15 +833,27 @@
int pointX = mXCenter + (int) (mLineLength[index] * Math.sin(selectionRadians));
int pointY = mYCenter - (int) (mLineLength[index] * Math.cos(selectionRadians));
+ int color;
+ int alpha;
+ Paint paint;
+
// Draw the selection circle
- canvas.drawCircle(pointX, pointY, mSelectionRadius[index],
- mPaintSelector[index % 2][SELECTOR_CIRCLE]);
+ color = mColorSelector[index % 2][SELECTOR_CIRCLE];
+ alpha = mAlphaSelector[index % 2][SELECTOR_CIRCLE].getValue();
+ paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
+ paint.setColor(color);
+ paint.setAlpha(getMultipliedAlpha(color, alpha));
+ canvas.drawCircle(pointX, pointY, mSelectionRadius[index], paint);
// Draw the dot if needed
if (mSelectionDegrees[index] % 30 != 0) {
// We're not on a direct tick
- canvas.drawCircle(pointX, pointY, (mSelectionRadius[index] * 2 / 7),
- mPaintSelector[index % 2][SELECTOR_DOT]);
+ color = mColorSelector[index % 2][SELECTOR_DOT];
+ alpha = mAlphaSelector[index % 2][SELECTOR_DOT].getValue();
+ paint = mPaintSelector[index % 2][SELECTOR_DOT];
+ paint.setColor(color);
+ paint.setAlpha(getMultipliedAlpha(color, alpha));
+ canvas.drawCircle(pointX, pointY, (mSelectionRadius[index] * 2 / 7), paint);
} else {
// We're not drawing the dot, so shorten the line to only go as far as the edge of the
// selection circle
@@ -820,8 +863,12 @@
}
// Draw the line
- canvas.drawLine(mXCenter, mYCenter, pointX, pointY,
- mPaintSelector[index % 2][SELECTOR_LINE]);
+ color = mColorSelector[index % 2][SELECTOR_LINE];
+ alpha = mAlphaSelector[index % 2][SELECTOR_LINE].getValue();
+ paint = mPaintSelector[index % 2][SELECTOR_LINE];
+ paint.setColor(color);
+ paint.setAlpha(getMultipliedAlpha(color, alpha));
+ canvas.drawLine(mXCenter, mYCenter, pointX, pointY, paint);
}
private void drawDebug(Canvas canvas) {
@@ -948,9 +995,11 @@
* Draw the 12 text values at the positions specified by the textGrid parameters.
*/
private void drawTextElements(Canvas canvas, float textSize, Typeface typeface, String[] texts,
- float[] textGridWidths, float[] textGridHeights, Paint paint) {
+ float[] textGridWidths, float[] textGridHeights, Paint paint, int color, int alpha) {
paint.setTextSize(textSize);
paint.setTypeface(typeface);
+ paint.setColor(color);
+ paint.setAlpha(getMultipliedAlpha(color, alpha));
canvas.drawText(texts[0], textGridWidths[3], textGridHeights[0], paint);
canvas.drawText(texts[1], textGridWidths[4], textGridHeights[1], paint);
canvas.drawText(texts[2], textGridWidths[5], textGridHeights[2], paint);
@@ -1023,17 +1072,17 @@
return animator;
}
- private static ObjectAnimator getFadeOutAnimator(Object target, int startAlpha, int endAlpha,
+ private static ObjectAnimator getFadeOutAnimator(IntHolder target, int startAlpha, int endAlpha,
InvalidateUpdateListener updateListener) {
int duration = 500;
- ObjectAnimator animator = ObjectAnimator.ofInt(target, "alpha", startAlpha, endAlpha);
+ ObjectAnimator animator = ObjectAnimator.ofInt(target, "value", startAlpha, endAlpha);
animator.setDuration(duration);
animator.addUpdateListener(updateListener);
return animator;
}
- private static ObjectAnimator getFadeInAnimator(Object target, int startAlpha, int endAlpha,
+ private static ObjectAnimator getFadeInAnimator(IntHolder target, int startAlpha, int endAlpha,
InvalidateUpdateListener updateListener) {
Keyframe kf0, kf1, kf2;
int duration = 500;
@@ -1048,7 +1097,7 @@
kf0 = Keyframe.ofInt(0f, startAlpha);
kf1 = Keyframe.ofInt(delayPoint, startAlpha);
kf2 = Keyframe.ofInt(1f, endAlpha);
- PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("alpha", kf0, kf1, kf2);
+ PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("value", kf0, kf1, kf2);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(
target, fadeIn).setDuration(totalDuration);
@@ -1068,25 +1117,25 @@
mHoursToMinutesAnims.add(getRadiusDisappearAnimator(this,
"animationRadiusMultiplierHours", mInvalidateUpdateListener,
mTransitionMidRadiusMultiplier, mTransitionEndRadiusMultiplier));
- mHoursToMinutesAnims.add(getFadeOutAnimator(mPaint[HOURS],
+ mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS],
ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeOutAnimator(mPaintSelector[HOURS][SELECTOR_CIRCLE],
+ mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
ALPHA_SELECTOR, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeOutAnimator(mPaintSelector[HOURS][SELECTOR_DOT],
+ mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeOutAnimator(mPaintSelector[HOURS][SELECTOR_LINE],
+ mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
ALPHA_SELECTOR, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
mHoursToMinutesAnims.add(getRadiusReappearAnimator(this,
"animationRadiusMultiplierMinutes", mInvalidateUpdateListener,
mTransitionMidRadiusMultiplier, mTransitionEndRadiusMultiplier));
- mHoursToMinutesAnims.add(getFadeInAnimator(mPaint[MINUTES],
+ mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES],
ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeInAnimator(mPaintSelector[MINUTES][SELECTOR_CIRCLE],
+ mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
ALPHA_TRANSPARENT, ALPHA_SELECTOR, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeInAnimator(mPaintSelector[MINUTES][SELECTOR_DOT],
+ mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeInAnimator(mPaintSelector[MINUTES][SELECTOR_LINE],
+ mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
ALPHA_TRANSPARENT, ALPHA_SELECTOR, mInvalidateUpdateListener));
}
@@ -1103,25 +1152,25 @@
mMinuteToHoursAnims.add(getRadiusDisappearAnimator(this,
"animationRadiusMultiplierMinutes", mInvalidateUpdateListener,
mTransitionMidRadiusMultiplier, mTransitionEndRadiusMultiplier));
- mMinuteToHoursAnims.add(getFadeOutAnimator(mPaint[MINUTES],
+ mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES],
ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeOutAnimator(mPaintSelector[MINUTES][SELECTOR_CIRCLE],
+ mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
ALPHA_SELECTOR, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeOutAnimator(mPaintSelector[MINUTES][SELECTOR_DOT],
+ mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeOutAnimator(mPaintSelector[MINUTES][SELECTOR_LINE],
+ mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
ALPHA_SELECTOR, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
mMinuteToHoursAnims.add(getRadiusReappearAnimator(this,
"animationRadiusMultiplierHours", mInvalidateUpdateListener,
mTransitionMidRadiusMultiplier, mTransitionEndRadiusMultiplier));
- mMinuteToHoursAnims.add(getFadeInAnimator(mPaint[HOURS],
+ mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS],
ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeInAnimator(mPaintSelector[HOURS][SELECTOR_CIRCLE],
+ mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
ALPHA_TRANSPARENT, ALPHA_SELECTOR, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeInAnimator(mPaintSelector[HOURS][SELECTOR_DOT],
+ mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeInAnimator(mPaintSelector[HOURS][SELECTOR_LINE],
+ mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
ALPHA_TRANSPARENT, ALPHA_SELECTOR, mInvalidateUpdateListener));
}
@@ -1393,4 +1442,20 @@
mInputEnabled = inputEnabled;
invalidate();
}
+
+ private static class IntHolder {
+ private int mValue;
+
+ public IntHolder(int value) {
+ mValue = value;
+ }
+
+ public void setValue(int value) {
+ mValue = value;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+ }
}
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index 99a7886..ac79d05 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -280,6 +280,7 @@
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_MULTIPLE_TASK |
Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
}
}
@@ -303,6 +304,7 @@
if (Intent.ACTION_SEND.equals(action) ||
Intent.ACTION_SEND_MULTIPLE.equals(action)) {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK |
Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
}
mContext.startActivity(launchIntent);
diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java
index 02033012..c8917e0 100644
--- a/core/java/android/widget/SuggestionsAdapter.java
+++ b/core/java/android/widget/SuggestionsAdapter.java
@@ -574,7 +574,7 @@
throw new FileNotFoundException("Failed to open " + uri);
}
try {
- return Drawable.createFromStreamThemed(stream, null, mContext.getTheme());
+ return Drawable.createFromStream(stream, null);
} finally {
try {
stream.close();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a4a9680e..84202eb 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -222,6 +222,7 @@
* @attr ref android.R.styleable#TextView_imeActionLabel
* @attr ref android.R.styleable#TextView_imeActionId
* @attr ref android.R.styleable#TextView_editorExtras
+ * @attr ref android.R.styleable#TextView_elegantTextHeight
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
@@ -2637,6 +2638,8 @@
* metrics, and also increases top and bottom bounds to provide more space.
*
* @param elegant set the paint's elegant metrics flag.
+ *
+ * @attr ref android.R.styleable#TextView_elegantTextHeight
*/
public void setElegantTextHeight(boolean elegant) {
mTextPaint.setElegantTextHeight(elegant);
@@ -5839,7 +5842,6 @@
int end = text.partialEndOffset;
if (end > N) end = N;
removeParcelableSpans(content, start, end);
- // If start > end, content.replace will swap them before using them.
content.replace(start, end, text.text);
}
}
diff --git a/core/java/android/widget/TimePickerDelegate.java b/core/java/android/widget/TimePickerDelegate.java
index 79256e5..bf3971c 100644
--- a/core/java/android/widget/TimePickerDelegate.java
+++ b/core/java/android/widget/TimePickerDelegate.java
@@ -140,13 +140,12 @@
mSelectMinutes = res.getString(R.string.select_minutes);
mHeaderSelectedColor = a.getColor(R.styleable.TimePicker_headerSelectedTextColor,
- android.R.color.holo_blue_light);
+ R.color.timepicker_default_selector_color_material);
- mHeaderUnSelectedColor = getUnselectedColor(
- R.color.timepicker_default_text_color_holo_light);
+ mHeaderUnSelectedColor = getUnselectedColor(R.color.timepicker_default_text_color_material);
if (mHeaderUnSelectedColor == -1) {
mHeaderUnSelectedColor = a.getColor(R.styleable.TimePicker_headerUnselectedTextColor,
- R.color.timepicker_default_text_color_holo_light);
+ R.color.timepicker_default_text_color_material);
}
final int headerBackgroundColor = a.getColor(
@@ -296,7 +295,7 @@
TypedArray a = mContext.obtainStyledAttributes(TEXT_APPEARANCE_TIME_LABEL_ATTR);
final int textAppearanceResId = a.getResourceId(0, 0);
tempView.setTextAppearance(mContext, (textAppearanceResId != 0) ?
- textAppearanceResId : R.style.TextAppearance_Holo_TimePicker_TimeLabel);
+ textAppearanceResId : R.style.TextAppearance_Material_TimePicker_TimeLabel);
a.recycle();
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 419c582..4f1cd68 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -146,6 +146,8 @@
private ActionMenuPresenter mOuterActionMenuPresenter;
private ExpandedActionViewMenuPresenter mExpandedMenuPresenter;
+ private boolean mCollapsible;
+
public Toolbar(Context context) {
this(context, null);
}
@@ -969,6 +971,23 @@
return child.getMeasuredWidth() + hMargins;
}
+ /**
+ * Returns true if the Toolbar is collapsible and has no child views with a measured size > 0.
+ */
+ private boolean shouldCollapse() {
+ if (!mCollapsible) return false;
+
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (shouldLayout(child) && child.getMeasuredWidth() > 0 &&
+ child.getMeasuredHeight() > 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
@@ -1093,7 +1112,8 @@
final int measuredHeight = resolveSizeAndState(
Math.max(height, getSuggestedMinimumHeight()),
heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT);
- setMeasuredDimension(measuredWidth, measuredHeight);
+
+ setMeasuredDimension(measuredWidth, shouldCollapse() ? 0 : measuredHeight);
}
@Override
@@ -1339,7 +1359,8 @@
return getPaddingTop();
case Gravity.BOTTOM:
- return getPaddingBottom() - child.getMeasuredHeight() - lp.bottomMargin;
+ return getHeight() - getPaddingBottom() -
+ child.getMeasuredHeight() - lp.bottomMargin;
default:
case Gravity.CENTER_VERTICAL:
@@ -1489,6 +1510,16 @@
}
/**
+ * Force the toolbar to collapse to zero-height during measurement if
+ * it could be considered "empty" (no visible elements with nonzero measured size)
+ * @hide
+ */
+ public void setCollapsible(boolean collapsible) {
+ mCollapsible = collapsible;
+ requestLayout();
+ }
+
+ /**
* Interface responsible for receiving menu item click events if the items themselves
* do not have individual item click listeners.
*/
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 106ac0b..877938e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -38,6 +38,7 @@
if (Intent.ACTION_SEND.equals(action) ||
Intent.ACTION_SEND_MULTIPLE.equals(action)) {
target.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK |
Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
}
}
@@ -61,6 +62,7 @@
if (Intent.ACTION_SEND.equals(action) ||
Intent.ACTION_SEND_MULTIPLE.equals(action)) {
in.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK |
Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
}
initialIntents[i] = in;
diff --git a/core/java/com/android/internal/app/NavItemSelectedListener.java b/core/java/com/android/internal/app/NavItemSelectedListener.java
new file mode 100644
index 0000000..545f44b
--- /dev/null
+++ b/core/java/com/android/internal/app/NavItemSelectedListener.java
@@ -0,0 +1,46 @@
+/*
+ * 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.internal.app;
+
+import android.app.ActionBar;
+import android.view.View;
+import android.widget.AdapterView;
+
+/**
+ * Wrapper to adapt the ActionBar.OnNavigationListener in an AdapterView.OnItemSelectedListener
+ * for use in Spinner widgets. Used by action bar implementations.
+ */
+class NavItemSelectedListener implements AdapterView.OnItemSelectedListener {
+ private final ActionBar.OnNavigationListener mListener;
+
+ public NavItemSelectedListener(ActionBar.OnNavigationListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ if (mListener != null) {
+ mListener.onNavigationItemSelected(position, id);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Do nothing
+ }
+}
diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java
index 6056bf2..e8a3f0a 100644
--- a/core/java/com/android/internal/app/ToolbarActionBar.java
+++ b/core/java/com/android/internal/app/ToolbarActionBar.java
@@ -173,14 +173,19 @@
@Override
public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
- throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback));
}
@Override
public void setSelectedNavigationItem(int position) {
- throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ switch (mDecorToolbar.getNavigationMode()) {
+ case NAVIGATION_MODE_LIST:
+ mDecorToolbar.setDropdownSelectedPosition(position);
+ break;
+ default:
+ throw new IllegalStateException(
+ "setSelectedNavigationIndex not valid for current navigation mode");
+ }
}
@Override
@@ -276,8 +281,10 @@
@Override
public void setNavigationMode(@NavigationMode int mode) {
- throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ if (mode == ActionBar.NAVIGATION_MODE_TABS) {
+ throw new IllegalArgumentException("Tabs not supported in this configuration");
+ }
+ mDecorToolbar.setNavigationMode(mode);
}
@Override
@@ -288,67 +295,67 @@
@Override
public Tab newTab() {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public void addTab(Tab tab) {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public void addTab(Tab tab, boolean setSelected) {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public void addTab(Tab tab, int position) {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public void addTab(Tab tab, int position, boolean setSelected) {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public void removeTab(Tab tab) {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public void removeTabAt(int position) {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public void removeAllTabs() {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public void selectTab(Tab tab) {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public Tab getSelectedTab() {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
public Tab getTabAt(int index) {
throw new UnsupportedOperationException(
- "Navigation modes are not supported in toolbar action bars");
+ "Tabs are not supported in toolbar action bars");
}
@Override
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index c0b5b97..87a80ac 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -18,9 +18,7 @@
import android.animation.ValueAnimator;
import android.content.res.TypedArray;
-import android.view.ViewGroup;
import android.view.ViewParent;
-import android.widget.AdapterView;
import android.widget.Toolbar;
import com.android.internal.R;
import com.android.internal.view.ActionBarPolicy;
@@ -30,7 +28,6 @@
import com.android.internal.widget.ActionBarContainer;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.ActionBarOverlayLayout;
-import com.android.internal.widget.ActionBarView;
import com.android.internal.widget.DecorToolbar;
import com.android.internal.widget.ScrollingTabContainerView;
@@ -59,7 +56,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.widget.SpinnerAdapter;
-import com.android.internal.widget.ToolbarWidgetWrapper;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -1313,23 +1309,4 @@
}
}
- static class NavItemSelectedListener implements AdapterView.OnItemSelectedListener {
- private final OnNavigationListener mListener;
-
- public NavItemSelectedListener(OnNavigationListener listener) {
- mListener = listener;
- }
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if (mListener != null) {
- mListener.onNavigationItemSelected(position, id);
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Do nothing
- }
- }
}
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index ba419f9..dab3aff 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -20,6 +20,7 @@
import android.util.Slog;
import java.io.File;
+import java.io.IOException;
/**
* Native libraries helper.
@@ -141,4 +142,18 @@
return deletedFiles;
}
+
+ // We don't care about the other return values for now.
+ private static final int BITCODE_PRESENT = 1;
+
+ public static boolean hasRenderscriptBitcode(ApkHandle handle) throws IOException {
+ final int returnVal = hasRenderscriptBitcode(handle.apkHandle);
+ if (returnVal < 0) {
+ throw new IOException("Error scanning APK, code: " + returnVal);
+ }
+
+ return (returnVal == BITCODE_PRESENT);
+ }
+
+ private static native int hasRenderscriptBitcode(long apkHandle);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 7dbde69..fdd24a6 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -468,7 +468,7 @@
return new InputMethodSubtypeSwitchingController(settings, context);
}
- public void onCommitTextLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
+ public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
if (mController == null) {
if (DEBUG) {
Log.e(TAG, "mController shouldn't be null.");
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 0d00f41..73d3738 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -17,8 +17,10 @@
package com.android.internal.net;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -45,7 +47,10 @@
public static Intent getIntentForConfirmation() {
Intent intent = new Intent();
- intent.setClassName(DIALOGS_PACKAGE, DIALOGS_PACKAGE + ".ConfirmDialog");
+ ComponentName componentName = ComponentName.unflattenFromString(
+ Resources.getSystem().getString(
+ com.android.internal.R.string.config_customVpnConfirmDialogComponent));
+ intent.setClassName(componentName.getPackageName(), componentName.getClassName());
return intent;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ed9f9bc..240d520 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2006,6 +2006,11 @@
}
}
+ @Override
+ public void commitCurrentHistoryBatchLocked() {
+ mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
+ }
+
void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
if (!mHaveBatteryLevel || !mRecordingHistory) {
return;
@@ -2342,13 +2347,16 @@
// Only care about partial wake locks, since full wake locks
// will be canceled when the user puts the screen to sleep.
aggregateLastWakeupUptimeLocked(uptime);
+ if (historyName == null) {
+ historyName = name;
+ }
if (mRecordAllWakeLocks) {
- if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, name, uid, 0)) {
+ if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName,
+ uid, 0)) {
addHistoryEventLocked(elapsedRealtime, uptime,
- HistoryItem.EVENT_WAKE_LOCK_START, name, uid);
+ HistoryItem.EVENT_WAKE_LOCK_START, historyName, uid);
}
}
- historyName = historyName == null ? name : historyName;
if (mWakeLockNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
@@ -2358,7 +2366,8 @@
mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
mWakeLockImportant = !unimportantForLogging;
addHistoryRecordLocked(elapsedRealtime, uptime);
- } else if (!mWakeLockImportant && !unimportantForLogging) {
+ } else if (!mWakeLockImportant && !unimportantForLogging
+ && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE) {
if (mHistoryLastWritten.wakelockTag != null) {
// We'll try to update the last tag.
mHistoryLastWritten.wakelockTag = null;
@@ -2386,9 +2395,13 @@
if (type == WAKE_TYPE_PARTIAL) {
mWakeLockNesting--;
if (mRecordAllWakeLocks) {
- if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid, 0)) {
+ if (historyName == null) {
+ historyName = name;
+ }
+ if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName,
+ uid, 0)) {
addHistoryEventLocked(elapsedRealtime, uptime,
- HistoryItem.EVENT_WAKE_LOCK_FINISH, name, uid);
+ HistoryItem.EVENT_WAKE_LOCK_FINISH, historyName, uid);
}
}
if (mWakeLockNesting == 0) {
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index 7edf4cc..c977997 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -45,6 +45,7 @@
public Object arg3;
public Object arg4;
public Object arg5;
+ public Object arg6;
public int argi1;
public int argi2;
public int argi3;
@@ -95,6 +96,7 @@
arg3 = null;
arg4 = null;
arg5 = null;
+ arg6 = null;
argi1 = 0;
argi2 = 0;
argi3 = 0;
diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index f38cbde..665055c 100644
--- a/core/java/com/android/internal/util/NotificationColorUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -35,7 +35,7 @@
import java.util.WeakHashMap;
/**
- * Helper class to process legacy (Holo) notifications to make them look like quantum notifications.
+ * Helper class to process legacy (Holo) notifications to make them look like material notifications.
*
* @hide
*/
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 9e8d12b..b100d27 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -28,4 +28,5 @@
void onUnbindMethod(int sequence);
void setActive(boolean active);
void setCursorAnchorMonitorMode(int monitorMode);
+ void setUserActionNotificationSequenceNumber(int sequenceNumber);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5336174..b84c359 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -77,6 +77,6 @@
boolean setInputMethodEnabled(String id, boolean enabled);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
int getInputMethodWindowVisibleHeight();
- oneway void notifyTextCommitted();
+ oneway void notifyUserAction(int sequenceNumber);
void setCursorAnchorMonitorMode(in IBinder token, int monitorMode);
}
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 14afe21..3a3e56d 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -47,13 +47,19 @@
* Sequence number of this binding.
*/
public final int sequence;
-
+
+ /**
+ * Sequence number of user action notification.
+ */
+ public final int userActionNotificationSequenceNumber;
+
public InputBindResult(IInputMethodSession _method, InputChannel _channel,
- String _id, int _sequence) {
+ String _id, int _sequence, int _userActionNotificationSequenceNumber) {
method = _method;
channel = _channel;
id = _id;
sequence = _sequence;
+ userActionNotificationSequenceNumber = _userActionNotificationSequenceNumber;
}
InputBindResult(Parcel source) {
@@ -65,12 +71,15 @@
}
id = source.readString();
sequence = source.readInt();
+ userActionNotificationSequenceNumber = source.readInt();
}
@Override
public String toString() {
return "InputBindResult{" + method + " " + id
- + " #" + sequence + "}";
+ + " sequence:" + sequence
+ + " userActionNotificationSequenceNumber:" + userActionNotificationSequenceNumber
+ + "}";
}
/**
@@ -90,6 +99,7 @@
}
dest.writeString(id);
dest.writeInt(sequence);
+ dest.writeInt(userActionNotificationSequenceNumber);
}
/**
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 8a9cb22..ea36e37 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -20,6 +20,7 @@
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -246,6 +247,13 @@
}
@Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ init(getContext());
+ requestApplyInsets();
+ }
+
+ @Override
public void onWindowSystemUiVisibilityChanged(int visible) {
super.onWindowSystemUiVisibilityChanged(visible);
pullChildren();
@@ -329,7 +337,7 @@
// insets in all cases, we need to know the measured size of the various action
// bar elements. onApplyWindowInsets() happens before the measure pass, so we can't
// do that here. Instead we will take this up in onMeasure().
- return WindowInsets.EMPTY;
+ return WindowInsets.CONSUMED;
}
@Override
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index af82778..77559c0 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -349,10 +349,7 @@
return mIncludeTabs;
}
- public void setEmbeddedTabView(View view) {
- setEmbeddedTabView((ScrollingTabContainerView) view);
- }
-
+ @Override
public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
if (mTabScrollView != null) {
removeView(mTabScrollView);
@@ -758,6 +755,7 @@
mNavItemSelectedListener = l;
if (mSpinner != null) {
mSpinner.setAdapter(adapter);
+ mSpinner.setOnItemSelectedListener(l);
}
}
diff --git a/core/java/com/android/internal/widget/DecorToolbar.java b/core/java/com/android/internal/widget/DecorToolbar.java
index ee6988e..5281045 100644
--- a/core/java/com/android/internal/widget/DecorToolbar.java
+++ b/core/java/com/android/internal/widget/DecorToolbar.java
@@ -71,7 +71,7 @@
int getDisplayOptions();
void setDisplayOptions(int opts);
- void setEmbeddedTabView(View tabView);
+ void setEmbeddedTabView(ScrollingTabContainerView tabView);
boolean hasEmbeddedTabs();
boolean isTitleTruncated();
void setCollapsible(boolean collapsible);
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index d841d53..60e649b 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -22,17 +22,18 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.Color;
+import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.os.Debug;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
-import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
@@ -110,14 +111,11 @@
private float mSquareWidth;
private float mSquareHeight;
- private Bitmap mBitmapBtnDefault;
- private Bitmap mBitmapBtnTouched;
- private Bitmap mBitmapCircleDefault;
- private Bitmap mBitmapCircleGreen;
- private Bitmap mBitmapCircleRed;
-
- private Bitmap mBitmapArrowGreenUp;
- private Bitmap mBitmapArrowRedUp;
+ private final Bitmap mBitmapBtnDefault;
+ private final Bitmap mBitmapBtnTouched;
+ private final Bitmap mBitmapCircleDefault;
+ private final Bitmap mBitmapCircleAlpha;
+ private final Bitmap mBitmapArrowAlphaUp;
private final Path mCurrentPath = new Path();
private final Rect mInvalidate = new Rect();
@@ -129,6 +127,10 @@
private int mAspect;
private final Matrix mArrowMatrix = new Matrix();
private final Matrix mCircleMatrix = new Matrix();
+ private final PorterDuffColorFilter mRegularColorFilter;
+ private final PorterDuffColorFilter mErrorColorFilter;
+ private final PorterDuffColorFilter mSuccessColorFilter;
+
/**
* Represents a cell in the 3 X 3 matrix of the unlock pattern view.
@@ -266,17 +268,22 @@
setClickable(true);
+
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true);
- int defaultColor = Color.WHITE;
- TypedValue outValue = new TypedValue();
- if (context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, outValue, true)) {
- defaultColor = context.getResources().getColor(outValue.resourceId);
- }
+ int regularColor = getResources().getColor(R.color.lock_pattern_view_regular_color);
+ int errorColor = getResources().getColor(R.color.lock_pattern_view_error_color);
+ int successColor = getResources().getColor(R.color.lock_pattern_view_success_color);
+ regularColor = a.getColor(R.styleable.LockPatternView_regularColor, regularColor);
+ errorColor = a.getColor(R.styleable.LockPatternView_errorColor, errorColor);
+ successColor = a.getColor(R.styleable.LockPatternView_successColor, successColor);
+ mRegularColorFilter = new PorterDuffColorFilter(regularColor, PorterDuff.Mode.SRC_ATOP);
+ mErrorColorFilter = new PorterDuffColorFilter(errorColor, PorterDuff.Mode.SRC_ATOP);
+ mSuccessColorFilter = new PorterDuffColorFilter(successColor, PorterDuff.Mode.SRC_ATOP);
- final int color = a.getColor(R.styleable.LockPatternView_pathColor, defaultColor);
- mPathPaint.setColor(color);
+ int pathColor = a.getColor(R.styleable.LockPatternView_pathColor, regularColor);
+ mPathPaint.setColor(pathColor);
mPathPaint.setAlpha(mStrokeAlpha);
mPathPaint.setStyle(Paint.Style.STROKE);
@@ -284,25 +291,26 @@
mPathPaint.setStrokeCap(Paint.Cap.ROUND);
// lot's of bitmaps!
- // TODO: those bitmaps are hardcoded to the Holo Theme which should not be the case!
- mBitmapBtnDefault = getBitmapFor(R.drawable.btn_code_lock_default_holo);
- mBitmapBtnTouched = getBitmapFor(R.drawable.btn_code_lock_touched_holo);
- mBitmapCircleDefault = getBitmapFor(R.drawable.indicator_code_lock_point_area_default_holo);
- mBitmapCircleGreen = getBitmapFor(R.drawable.indicator_code_lock_point_area_green_holo);
- mBitmapCircleRed = getBitmapFor(R.drawable.indicator_code_lock_point_area_red_holo);
-
- mBitmapArrowGreenUp = getBitmapFor(R.drawable.indicator_code_lock_drag_direction_green_up);
- mBitmapArrowRedUp = getBitmapFor(R.drawable.indicator_code_lock_drag_direction_red_up);
+ // TODO: those bitmaps are hardcoded to the Material Theme which should not be the case!
+ mBitmapBtnDefault = getBitmapFor(R.drawable.btn_code_lock_default_mtrl_alpha);
+ mBitmapBtnTouched = getBitmapFor(R.drawable.btn_code_lock_touched_mtrl_alpha);
+ mBitmapCircleDefault = getBitmapFor(
+ R.drawable.indicator_code_lock_point_area_default_mtrl_alpha);
+ mBitmapCircleAlpha = getBitmapFor(R.drawable.indicator_code_lock_point_area_mtrl_alpha);
+ mBitmapArrowAlphaUp = getBitmapFor(
+ R.drawable.indicator_code_lock_drag_direction_up_mtrl_alpha);
// bitmaps have the size of the largest bitmap in this group
final Bitmap bitmaps[] = { mBitmapBtnDefault, mBitmapBtnTouched, mBitmapCircleDefault,
- mBitmapCircleGreen, mBitmapCircleRed };
+ mBitmapCircleAlpha};
for (Bitmap bitmap : bitmaps) {
mBitmapWidth = Math.max(mBitmapWidth, bitmap.getWidth());
mBitmapHeight = Math.max(mBitmapHeight, bitmap.getHeight());
}
+ mPaint.setAntiAlias(true);
+ mPaint.setDither(true);
mPaint.setFilterBitmap(true);
mCellStates = new CellState[3][3];
@@ -963,7 +971,12 @@
}
private void drawArrow(Canvas canvas, float leftX, float topY, Cell start, Cell end) {
- boolean green = mPatternDisplayMode != DisplayMode.Wrong;
+ if (mPatternInProgress) {
+ mPaint.setColorFilter(mRegularColorFilter);
+ } else {
+ boolean success = mPatternDisplayMode != DisplayMode.Wrong;
+ mPaint.setColorFilter(success ? mSuccessColorFilter : mErrorColorFilter);
+ }
final int endRow = end.row;
final int startRow = start.row;
@@ -977,7 +990,6 @@
// compute transform to place arrow bitmaps at correct angle inside circle.
// This assumes that the arrow image is drawn at 12:00 with it's top edge
// coincident with the circle bitmap's top edge.
- Bitmap arrow = green ? mBitmapArrowGreenUp : mBitmapArrowRedUp;
final int cellWidth = mBitmapWidth;
final int cellHeight = mBitmapHeight;
@@ -994,8 +1006,8 @@
mArrowMatrix.preScale(sx, sy);
mArrowMatrix.preTranslate(-mBitmapWidth/2, -mBitmapHeight/2);
mArrowMatrix.preRotate(angle, cellWidth / 2.0f, cellHeight / 2.0f); // rotate about cell center
- mArrowMatrix.preTranslate((cellWidth - arrow.getWidth()) / 2.0f, 0.0f); // translate to 12:00 pos
- canvas.drawBitmap(arrow, mArrowMatrix, mPaint);
+ mArrowMatrix.preTranslate((cellWidth - mBitmapArrowAlphaUp.getWidth()) / 2.0f, 0.0f); // translate to 12:00 pos
+ canvas.drawBitmap(mBitmapArrowAlphaUp, mArrowMatrix, mPaint);
}
/**
@@ -1008,24 +1020,28 @@
boolean partOfPattern) {
Bitmap outerCircle;
Bitmap innerCircle;
-
+ ColorFilter outerFilter;
if (!partOfPattern || mInStealthMode) {
// unselected circle
outerCircle = mBitmapCircleDefault;
innerCircle = mBitmapBtnDefault;
+ outerFilter = mRegularColorFilter;
} else if (mPatternInProgress) {
// user is in middle of drawing a pattern
- outerCircle = mBitmapCircleGreen;
+ outerCircle = mBitmapCircleAlpha;
innerCircle = mBitmapBtnTouched;
+ outerFilter = mRegularColorFilter;
} else if (mPatternDisplayMode == DisplayMode.Wrong) {
// the pattern is wrong
- outerCircle = mBitmapCircleRed;
+ outerCircle = mBitmapCircleAlpha;
innerCircle = mBitmapBtnDefault;
+ outerFilter = mErrorColorFilter;
} else if (mPatternDisplayMode == DisplayMode.Correct ||
mPatternDisplayMode == DisplayMode.Animate) {
// the pattern is correct
- outerCircle = mBitmapCircleGreen;
+ outerCircle = mBitmapCircleAlpha;
innerCircle = mBitmapBtnDefault;
+ outerFilter = mSuccessColorFilter;
} else {
throw new IllegalStateException("unknown display mode " + mPatternDisplayMode);
}
@@ -1048,7 +1064,9 @@
mCircleMatrix.preScale(sx * scale, sy * scale);
mCircleMatrix.preTranslate(-mBitmapWidth/2, -mBitmapHeight/2);
+ mPaint.setColorFilter(outerFilter);
canvas.drawBitmap(outerCircle, mCircleMatrix, mPaint);
+ mPaint.setColorFilter(mRegularColorFilter);
canvas.drawBitmap(innerCircle, mCircleMatrix, mPaint);
}
diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java
index 117463a..2f987e9 100644
--- a/core/java/com/android/internal/widget/SubtitleView.java
+++ b/core/java/com/android/internal/widget/SubtitleView.java
@@ -271,10 +271,13 @@
style = CaptionStyle.PRESETS[styleId];
}
- mForegroundColor = style.foregroundColor;
- mBackgroundColor = style.backgroundColor;
- mEdgeType = style.edgeType;
- mEdgeColor = style.edgeColor;
+ final CaptionStyle defStyle = CaptionStyle.DEFAULT;
+ mForegroundColor = style.hasForegroundColor() ?
+ style.foregroundColor : defStyle.foregroundColor;
+ mBackgroundColor = style.hasBackgroundColor() ?
+ style.backgroundColor : defStyle.backgroundColor;
+ mEdgeType = style.hasEdgeType() ? style.edgeType : defStyle.edgeType;
+ mEdgeColor = style.hasEdgeColor() ? style.edgeColor : defStyle.edgeColor;
mHasMeasurements = false;
final Typeface typeface = style.getTypeface();
diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
index 3e15c32..7fb2efd 100644
--- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
+++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java
@@ -27,6 +27,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
@@ -78,6 +79,8 @@
private boolean mMenuPrepared;
private ActionMenuPresenter mActionMenuPresenter;
+ private int mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD;
+
public ToolbarWidgetWrapper(Toolbar toolbar) {
mToolbar = toolbar;
@@ -394,8 +397,19 @@
}
@Override
- public void setEmbeddedTabView(View tabView) {
+ public void setEmbeddedTabView(ScrollingTabContainerView tabView) {
+ if (mTabView != null && mTabView.getParent() == mToolbar) {
+ mToolbar.removeView(mTabView);
+ }
mTabView = tabView;
+ if (tabView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
+ mToolbar.addView(mTabView, 0);
+ Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams();
+ lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ lp.gravity = Gravity.START | Gravity.BOTTOM;
+ tabView.setAllowCollapse(true);
+ }
}
@Override
@@ -410,7 +424,7 @@
@Override
public void setCollapsible(boolean collapsible) {
- // Ignore
+ mToolbar.setCollapsible(collapsible);
}
@Override
@@ -420,23 +434,63 @@
@Override
public int getNavigationMode() {
- return 0;
+ return mNavigationMode;
}
@Override
public void setNavigationMode(int mode) {
- if (mode != ActionBar.NAVIGATION_MODE_STANDARD) {
- throw new IllegalArgumentException(
- "Navigation modes not supported in this configuration");
+ final int oldMode = mNavigationMode;
+ if (mode != oldMode) {
+ switch (oldMode) {
+ case ActionBar.NAVIGATION_MODE_LIST:
+ if (mSpinner != null && mSpinner.getParent() == mToolbar) {
+ mToolbar.removeView(mSpinner);
+ }
+ break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabView != null && mTabView.getParent() == mToolbar) {
+ mToolbar.removeView(mTabView);
+ }
+ break;
+ }
+
+ mNavigationMode = mode;
+
+ switch (mode) {
+ case ActionBar.NAVIGATION_MODE_STANDARD:
+ break;
+ case ActionBar.NAVIGATION_MODE_LIST:
+ ensureSpinner();
+ mToolbar.addView(mSpinner, 0);
+ break;
+ case ActionBar.NAVIGATION_MODE_TABS:
+ if (mTabView != null) {
+ mToolbar.addView(mTabView, 0);
+ Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams();
+ lp.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ lp.gravity = Gravity.START | Gravity.BOTTOM;
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid navigation mode " + mode);
+ }
+ }
+ }
+
+ private void ensureSpinner() {
+ if (mSpinner == null) {
+ mSpinner = new Spinner(getContext(), null, R.attr.actionDropDownStyle);
+ Toolbar.LayoutParams lp = new Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
+ mSpinner.setLayoutParams(lp);
}
}
@Override
public void setDropdownParams(SpinnerAdapter adapter,
AdapterView.OnItemSelectedListener listener) {
- if (mSpinner == null) {
- mSpinner = new Spinner(getContext());
- }
+ ensureSpinner();
mSpinner.setAdapter(adapter);
mSpinner.setOnItemSelectedListener(listener);
}
@@ -515,7 +569,7 @@
@Override
public void setNavigationIcon(int resId) {
- setNavigationIcon(mToolbar.getContext().getDrawable(resId));
+ setNavigationIcon(resId != 0 ? mToolbar.getContext().getDrawable(resId) : null);
}
@Override
diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java
index bf36bb1..43a05d0 100644
--- a/core/java/com/android/server/SystemService.java
+++ b/core/java/com/android/server/SystemService.java
@@ -193,58 +193,4 @@
private SystemServiceManager getManager() {
return LocalServices.getService(SystemServiceManager.class);
}
-
-// /**
-// * Called when a new user has been created. If your service deals with multiple users, this
-// * method should be overridden.
-// *
-// * @param userHandle The user that was created.
-// */
-// public void onUserCreated(int userHandle) {
-// }
-//
-// /**
-// * Called when an existing user has started a new session. If your service deals with multiple
-// * users, this method should be overridden.
-// *
-// * @param userHandle The user who started a new session.
-// */
-// public void onUserStarted(int userHandle) {
-// }
-//
-// /**
-// * Called when a background user session has entered the foreground. If your service deals with
-// * multiple users, this method should be overridden.
-// *
-// * @param userHandle The user who's session entered the foreground.
-// */
-// public void onUserForeground(int userHandle) {
-// }
-//
-// /**
-// * Called when a foreground user session has entered the background. If your service deals with
-// * multiple users, this method should be overridden;
-// *
-// * @param userHandle The user who's session entered the background.
-// */
-// public void onUserBackground(int userHandle) {
-// }
-//
-// /**
-// * Called when a user's active session has stopped. If your service deals with multiple users,
-// * this method should be overridden.
-// *
-// * @param userHandle The user who's session has stopped.
-// */
-// public void onUserStopped(int userHandle) {
-// }
-//
-// /**
-// * Called when a user has been removed from the system. If your service deals with multiple
-// * users, this method should be overridden.
-// *
-// * @param userHandle The user who has been removed.
-// */
-// public void onUserRemoved(int userHandle) {
-// }
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index f446c3a..15dfed1 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -57,7 +57,6 @@
android_view_KeyEvent.cpp \
android_view_KeyCharacterMap.cpp \
android_view_GraphicBuffer.cpp \
- android_view_GLRenderer.cpp \
android_view_GLES20Canvas.cpp \
android_view_HardwareLayer.cpp \
android_view_ThreadedRenderer.cpp \
@@ -90,6 +89,7 @@
android_util_Process.cpp \
android_util_StringBlock.cpp \
android_util_XmlBlock.cpp \
+ android/graphics/AndroidPicture.cpp \
android/graphics/AutoDecodeCancel.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
@@ -141,6 +141,7 @@
android_hardware_camera2_DngCreator.cpp \
android_hardware_SensorManager.cpp \
android_hardware_SerialPort.cpp \
+ android_hardware_SoundTrigger.cpp \
android_hardware_UsbDevice.cpp \
android_hardware_UsbDeviceConnection.cpp \
android_hardware_UsbRequest.cpp \
@@ -236,6 +237,7 @@
libpdfium \
libimg_utils \
libnetd_client \
+ libsoundtrigger
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e069876..f2b9bac 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -29,7 +29,6 @@
#include <SkGraphics.h>
#include <SkImageDecoder.h>
-#include <SkImageRef_GlobalPool.h>
#include "jni.h"
#include "JNIHelp.h"
@@ -83,6 +82,7 @@
extern int register_android_hardware_camera2_DngCreator(JNIEnv *env);
extern int register_android_hardware_SensorManager(JNIEnv *env);
extern int register_android_hardware_SerialPort(JNIEnv *env);
+extern int register_android_hardware_SoundTrigger(JNIEnv *env);
extern int register_android_hardware_UsbDevice(JNIEnv *env);
extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env);
extern int register_android_hardware_UsbRequest(JNIEnv *env);
@@ -130,7 +130,6 @@
extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
extern int register_android_view_GraphicBuffer(JNIEnv* env);
extern int register_android_view_GLES20Canvas(JNIEnv* env);
-extern int register_android_view_GLRenderer(JNIEnv* env);
extern int register_android_view_HardwareLayer(JNIEnv* env);
extern int register_android_view_ThreadedRenderer(JNIEnv* env);
extern int register_android_view_Surface(JNIEnv* env);
@@ -245,14 +244,6 @@
mArgBlockLength(argBlockLength)
{
SkGraphics::Init();
- // this sets our preference for 16bit images during decode
- // in case the src is opaque and 24bit
- SkImageDecoder::SetDeviceConfig(SkBitmap::kRGB_565_Config);
- // This cache is shared between browser native images, and java "purgeable"
- // bitmaps. This globalpool is for images that do not either use the java
- // heap, or are not backed by ashmem. See BitmapFactory.cpp for the key
- // java call site.
- SkImageRef_GlobalPool::SetRAMBudget(512 * 1024);
// There is also a global font cache, but its budget is specified in code
// see SkFontHost_android.cpp
@@ -499,6 +490,8 @@
char profile_duration[sizeof("-Xprofile-duration:") + PROPERTY_VALUE_MAX];
char profile_interval[sizeof("-Xprofile-interval:") + PROPERTY_VALUE_MAX];
char profile_backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
+ char profile_top_k_threshold[sizeof("-Xprofile-top-k-threshold") + PROPERTY_VALUE_MAX];
+ char profile_top_k_change_threshold[sizeof("-Xprofile-top-k-change-threshold") + PROPERTY_VALUE_MAX];
char langOption[sizeof("-Duser.language=") + 3];
char regionOption[sizeof("-Duser.region=") + 3];
char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
@@ -822,31 +815,65 @@
* Set profiler options
*/
if (libart) {
- // Number of seconds during profile runs.
- strcpy(profile_period, "-Xprofile-period:");
- property_get("dalvik.vm.profile.period_secs", profile_period+17, "10");
- opt.optionString = profile_period;
- mOptions.add(opt);
+ // Whether or not the profiler should be enabled.
+ property_get("dalvik.vm.profiler", propBuf, "0");
+ if (propBuf[0] == '1') {
+ opt.optionString = "-Xenable-profiler";
+ mOptions.add(opt);
+ }
- // Length of each profile run (seconds).
- strcpy(profile_duration, "-Xprofile-duration:");
- property_get("dalvik.vm.profile.duration_secs", profile_duration+19, "30");
- opt.optionString = profile_duration;
- mOptions.add(opt);
+ // Whether the profile should start upon app startup or be delayed by some random offset
+ // (in seconds) that is bound between 0 and a fixed value.
+ property_get("dalvik.vm.profile.start-immed", propBuf, "0");
+ if (propBuf[0] == '1') {
+ opt.optionString = "-Xprofile-start-immediately";
+ mOptions.add(opt);
+ }
+ // Number of seconds during profile runs.
+ strcpy(profile_period, "-Xprofile-period:");
+ if (property_get("dalvik.vm.profile.period-secs", profile_period+17, NULL) > 0) {
+ opt.optionString = profile_period;
+ mOptions.add(opt);
+ }
- // Polling interval during profile run (microseconds).
- strcpy(profile_interval, "-Xprofile-interval:");
- property_get("dalvik.vm.profile.interval_us", profile_interval+19, "10000");
- opt.optionString = profile_interval;
- mOptions.add(opt);
+ // Length of each profile run (seconds).
+ strcpy(profile_duration, "-Xprofile-duration:");
+ if (property_get("dalvik.vm.profile.duration-secs", profile_duration+19, NULL) > 0) {
+ opt.optionString = profile_duration;
+ mOptions.add(opt);
+ }
- // Coefficient for period backoff. The the period is multiplied
- // by this value after each profile run.
- strcpy(profile_backoff, "-Xprofile-backoff:");
- property_get("dalvik.vm.profile.backoff_coeff", profile_backoff+18, "2.0");
- opt.optionString = profile_backoff;
- mOptions.add(opt);
+ // Polling interval during profile run (microseconds).
+ strcpy(profile_interval, "-Xprofile-interval:");
+ if (property_get("dalvik.vm.profile.interval-us", profile_interval+19, NULL) > 0) {
+ opt.optionString = profile_interval;
+ mOptions.add(opt);
+ }
+
+ // Coefficient for period backoff. The the period is multiplied
+ // by this value after each profile run.
+ strcpy(profile_backoff, "-Xprofile-backoff:");
+ if (property_get("dalvik.vm.profile.backoff-coeff", profile_backoff+18, NULL) > 0) {
+ opt.optionString = profile_backoff;
+ mOptions.add(opt);
+ }
+
+ // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
+ strcpy(profile_top_k_threshold, "-Xprofile-top-k-threshold:");
+ if (property_get("dalvik.vm.profile.top-k-thr", profile_top_k_threshold+26, NULL) > 0) {
+ opt.optionString = profile_top_k_threshold;
+ mOptions.add(opt);
+ }
+
+ // The threshold after which a change in the structure of the top K% profiled samples becomes significant
+ // and triggers recompilation. A change in profile is considered significant if X% (top-k-change-threshold)
+ // of the top K% (top-k-threshold property) samples has changed.
+ strcpy(profile_top_k_change_threshold, "-Xprofile-top-k-change-threshold:");
+ if (property_get("dalvik.vm.profile.top-k-ch-thr", profile_top_k_change_threshold+33, NULL) > 0) {
+ opt.optionString = profile_top_k_change_threshold;
+ mOptions.add(opt);
+ }
}
initArgs.version = JNI_VERSION_1_4;
@@ -1214,7 +1241,6 @@
REG_JNI(register_android_view_RenderNodeAnimator),
REG_JNI(register_android_view_GraphicBuffer),
REG_JNI(register_android_view_GLES20Canvas),
- REG_JNI(register_android_view_GLRenderer),
REG_JNI(register_android_view_HardwareLayer),
REG_JNI(register_android_view_ThreadedRenderer),
REG_JNI(register_android_view_Surface),
@@ -1290,6 +1316,7 @@
REG_JNI(register_android_hardware_camera2_DngCreator),
REG_JNI(register_android_hardware_SensorManager),
REG_JNI(register_android_hardware_SerialPort),
+ REG_JNI(register_android_hardware_SoundTrigger),
REG_JNI(register_android_hardware_UsbDevice),
REG_JNI(register_android_hardware_UsbDeviceConnection),
REG_JNI(register_android_hardware_UsbRequest),
diff --git a/core/jni/android/graphics/AndroidPicture.cpp b/core/jni/android/graphics/AndroidPicture.cpp
new file mode 100644
index 0000000..5977ab2
--- /dev/null
+++ b/core/jni/android/graphics/AndroidPicture.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AndroidPicture.h"
+#include "SkCanvas.h"
+#include "SkStream.h"
+
+AndroidPicture::AndroidPicture(const AndroidPicture* src) {
+ if (NULL != src) {
+ mWidth = src->width();
+ mHeight = src->height();
+ if (NULL != src->mPicture.get()) {
+ mPicture.reset(SkRef(src->mPicture.get()));
+ } if (NULL != src->mRecorder.get()) {
+ mPicture.reset(src->makePartialCopy());
+ }
+ } else {
+ mWidth = 0;
+ mHeight = 0;
+ }
+}
+
+SkCanvas* AndroidPicture::beginRecording(int width, int height) {
+ mPicture.reset(NULL);
+ mRecorder.reset(new SkPictureRecorder);
+ mWidth = width;
+ mHeight = height;
+ return mRecorder->beginRecording(width, height, NULL, 0);
+}
+
+void AndroidPicture::endRecording() {
+ if (NULL != mRecorder.get()) {
+ mPicture.reset(mRecorder->endRecording());
+ mRecorder.reset(NULL);
+ }
+}
+
+int AndroidPicture::width() const {
+ if (NULL != mPicture.get()) {
+ SkASSERT(mPicture->width() == mWidth);
+ SkASSERT(mPicture->height() == mHeight);
+ }
+
+ return mWidth;
+}
+
+int AndroidPicture::height() const {
+ if (NULL != mPicture.get()) {
+ SkASSERT(mPicture->width() == mWidth);
+ SkASSERT(mPicture->height() == mHeight);
+ }
+
+ return mHeight;
+}
+
+AndroidPicture* AndroidPicture::CreateFromStream(SkStream* stream) {
+ AndroidPicture* newPict = new AndroidPicture;
+
+ newPict->mPicture.reset(SkPicture::CreateFromStream(stream));
+ if (NULL != newPict->mPicture.get()) {
+ newPict->mWidth = newPict->mPicture->width();
+ newPict->mHeight = newPict->mPicture->height();
+ }
+
+ return newPict;
+}
+
+void AndroidPicture::serialize(SkWStream* stream) const {
+ if (NULL != mRecorder.get()) {
+ SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy());
+ tempPict->serialize(stream);
+ } else if (NULL != mPicture.get()) {
+ mPicture->serialize(stream);
+ } else {
+ SkPicture empty;
+ empty.serialize(stream);
+ }
+}
+
+void AndroidPicture::draw(SkCanvas* canvas) {
+ if (NULL != mRecorder.get()) {
+ this->endRecording();
+ SkASSERT(NULL != mPicture.get());
+ }
+ if (NULL != mPicture.get()) {
+ // TODO: remove this const_cast once pictures are immutable
+ const_cast<SkPicture*>(mPicture.get())->draw(canvas);
+ }
+}
+
+SkPicture* AndroidPicture::makePartialCopy() const {
+ SkASSERT(NULL != mRecorder.get());
+
+ SkPictureRecorder reRecorder;
+
+ SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
+ mRecorder->partialReplay(canvas);
+ return reRecorder.endRecording();
+}
diff --git a/core/jni/android/graphics/AndroidPicture.h b/core/jni/android/graphics/AndroidPicture.h
new file mode 100644
index 0000000..f434941
--- /dev/null
+++ b/core/jni/android/graphics/AndroidPicture.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_PICTURE_H
+#define ANDROID_PICTURE_H
+
+#include "SkPicture.h"
+#include "SkPictureRecorder.h"
+#include "SkRefCnt.h"
+#include "SkTemplates.h"
+
+class SkCanvas;
+class SkPicture;
+class SkPictureRecorder;
+class SkStream;
+class SkWStream;
+
+// Skia's SkPicture class has been split into an SkPictureRecorder
+// and an SkPicture. AndroidPicture recreates the functionality
+// of the old SkPicture interface by flip-flopping between the two
+// new classes.
+class AndroidPicture {
+public:
+ explicit AndroidPicture(const AndroidPicture* src = NULL);
+
+ SkCanvas* beginRecording(int width, int height);
+
+ void endRecording();
+
+ int width() const;
+
+ int height() const;
+
+ static AndroidPicture* CreateFromStream(SkStream* stream);
+
+ void serialize(SkWStream* stream) const;
+
+ void draw(SkCanvas* canvas);
+
+private:
+ int mWidth;
+ int mHeight;
+ SkAutoTUnref<const SkPicture> mPicture;
+ SkAutoTDelete<SkPictureRecorder> mRecorder;
+
+ // Make a copy of a picture that is in the midst of being recorded. The
+ // resulting picture will have balanced saves and restores.
+ SkPicture* makePartialCopy() const;
+};
+#endif // ANDROID_PICTURE_H
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 0328517..9998995 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -361,24 +361,50 @@
}
static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
- jint width, jint height, jint configHandle, jint allocSize) {
+ jint width, jint height, jint configHandle, jint allocSize,
+ jboolean requestPremul) {
SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
- if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {
+ SkColorType colorType = SkBitmapConfigToColorType(config);
+
+ // ARGB_4444 is a deprecated format, convert automatically to 8888
+ if (colorType == kARGB_4444_SkColorType) {
+ colorType = kN32_SkColorType;
+ }
+
+ if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) {
// done in native as there's no way to get BytesPerPixel in Java
doThrowIAE(env, "Bitmap not large enough to support new configuration");
return;
}
SkPixelRef* ref = bitmap->pixelRef();
- SkSafeRef(ref);
- bitmap->setConfig(config, width, height);
+ ref->ref();
+ SkAlphaType alphaType;
+ if (bitmap->colorType() != kRGB_565_SkColorType
+ && bitmap->alphaType() == kOpaque_SkAlphaType) {
+ // If the original bitmap was set to opaque, keep that setting, unless it
+ // was 565, which is required to be opaque.
+ alphaType = kOpaque_SkAlphaType;
+ } else {
+ // Otherwise respect the premultiplied request.
+ alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
+ }
+ bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType));
+ // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for
+ // its alphatype), so it would make more sense from Skia's perspective to create a
+ // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key
+ // for its cache, so it won't realize this is the same Java Bitmap.
+ SkImageInfo& info = const_cast<SkImageInfo&>(ref->info());
+ // Use the updated from the SkBitmap, which may have corrected an invalid alphatype.
+ // (e.g. 565 non-opaque)
+ info = bitmap->info();
bitmap->setPixelRef(ref);
// notifyPixelsChanged will increment the generation ID even though the actual pixel data
// hasn't been touched. This signals the renderer that the bitmap (including width, height,
- // and config) has changed.
+ // colortype and alphatype) has changed.
ref->notifyPixelsChanged();
- SkSafeUnref(ref);
+ ref->unref();
}
// These must match the int values in Bitmap.java
@@ -799,7 +825,7 @@
(void*)Bitmap_copy },
{ "nativeDestructor", "(J)V", (void*)Bitmap_destructor },
{ "nativeRecycle", "(J)Z", (void*)Bitmap_recycle },
- { "nativeReconfigure", "(JIIII)V", (void*)Bitmap_reconfigure },
+ { "nativeReconfigure", "(JIIIIZ)V", (void*)Bitmap_reconfigure },
{ "nativeCompress", "(JIILjava/io/OutputStream;[B)Z",
(void*)Bitmap_compress },
{ "nativeErase", "(JI)V", (void*)Bitmap_erase },
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 928a7f8..5106f0d 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -2,11 +2,8 @@
#include "BitmapFactory.h"
#include "NinePatchPeeker.h"
-#include "SkData.h"
#include "SkFrontBufferedStream.h"
#include "SkImageDecoder.h"
-#include "SkImageRef_ashmem.h"
-#include "SkImageRef_GlobalPool.h"
#include "SkMath.h"
#include "SkPixelRef.h"
#include "SkStream.h"
@@ -32,8 +29,6 @@
jfieldID gOptions_premultipliedFieldID;
jfieldID gOptions_mutableFieldID;
jfieldID gOptions_ditherFieldID;
-jfieldID gOptions_purgeableFieldID;
-jfieldID gOptions_shareableFieldID;
jfieldID gOptions_preferQualityOverSpeedFieldID;
jfieldID gOptions_scaledFieldID;
jfieldID gOptions_densityFieldID;
@@ -90,14 +85,6 @@
return jstr;
}
-static bool optionsPurgeable(JNIEnv* env, jobject options) {
- return options != NULL && env->GetBooleanField(options, gOptions_purgeableFieldID);
-}
-
-static bool optionsShareable(JNIEnv* env, jobject options) {
- return options != NULL && env->GetBooleanField(options, gOptions_shareableFieldID);
-}
-
static bool optionsJustBounds(JNIEnv* env, jobject options) {
return options != NULL && env->GetBooleanField(options, gOptions_justBoundsFieldID);
}
@@ -125,27 +112,6 @@
}
}
-static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStreamRewindable* stream,
- int sampleSize, bool ditherImage) {
-
- if (kUnknown_SkColorType == bitmap->colorType()) {
- ALOGW("bitmap has unknown configuration so no memory has been allocated");
- return NULL;
- }
-
- SkImageRef* pr;
- // only use ashmem for large images, since mmaps come at a price
- if (bitmap->getSize() >= 32 * 1024) {
- pr = new SkImageRef_ashmem(bitmap->info(), stream, sampleSize);
- } else {
- pr = new SkImageRef_GlobalPool(bitmap->info(), stream, sampleSize);
- }
- pr->setDitherImage(ditherImage);
- bitmap->setPixelRef(pr)->unref();
- pr->isOpaque(bitmap);
- return pr;
-}
-
static SkColorType colorTypeForScaledOutput(SkColorType colorType) {
switch (colorType) {
case kUnknown_SkColorType:
@@ -229,21 +195,17 @@
const unsigned int mSize;
};
-// since we "may" create a purgeable imageref, we require the stream be ref'able
-// i.e. dynamically allocated, since its lifetime may exceed the current stack
-// frame.
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding,
- jobject options, bool allowPurgeable, bool forcePurgeable = false) {
+ jobject options) {
int sampleSize = 1;
- SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode;
+ SkImageDecoder::Mode decodeMode = SkImageDecoder::kDecodePixels_Mode;
SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
bool doDither = true;
bool isMutable = false;
float scale = 1.0f;
- bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
bool preferQualityOverSpeed = false;
bool requireUnpremultiplied = false;
@@ -252,7 +214,7 @@
if (options != NULL) {
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
if (optionsJustBounds(env, options)) {
- mode = SkImageDecoder::kDecodeBounds_Mode;
+ decodeMode = SkImageDecoder::kDecodeBounds_Mode;
}
// initialize these, in case we fail later on
@@ -280,7 +242,6 @@
}
const bool willScale = scale != 1.0f;
- isPurgeable &= !willScale;
SkImageDecoder* decoder = SkImageDecoder::Factory(stream);
if (decoder == NULL) {
@@ -311,8 +272,6 @@
NinePatchPeeker peeker(decoder);
decoder->setPeeker(&peeker);
- SkImageDecoder::Mode decodeMode = isPurgeable ? SkImageDecoder::kDecodeBounds_Mode : mode;
-
JavaPixelAllocator javaAllocator(env);
RecyclingPixelAllocator recyclingAllocator(outputBitmap->pixelRef(), existingBufferSize);
ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
@@ -353,7 +312,7 @@
int scaledWidth = decodingBitmap.width();
int scaledHeight = decodingBitmap.height();
- if (willScale && mode != SkImageDecoder::kDecodeBounds_Mode) {
+ if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
scaledWidth = int(scaledWidth * scale + 0.5f);
scaledHeight = int(scaledHeight * scale + 0.5f);
}
@@ -367,7 +326,7 @@
}
// if we're in justBounds mode, return now (skip the java bitmap)
- if (mode == SkImageDecoder::kDecodeBounds_Mode) {
+ if (decodeMode == SkImageDecoder::kDecodeBounds_Mode) {
return NULL;
}
@@ -459,21 +418,15 @@
}
}
- SkPixelRef* pr;
- if (isPurgeable) {
- pr = installPixelRef(outputBitmap, stream, sampleSize, doDither);
- } else {
- // if we get here, we're in kDecodePixels_Mode and will therefore
- // already have a pixelref installed.
- pr = outputBitmap->pixelRef();
- }
- if (pr == NULL) {
+ // if we get here, we're in kDecodePixels_Mode and will therefore
+ // already have a pixelref installed.
+ if (outputBitmap->pixelRef() == NULL) {
return nullObjectReturn("Got null SkPixelRef");
}
if (!isMutable && javaBitmap == NULL) {
// promise we will never change our pixels (great for sharing and pictures)
- pr->setImmutable();
+ outputBitmap->setImmutable();
}
// detach bitmap from its autodeleter, since we want to own it now
@@ -512,8 +465,7 @@
SkAutoTUnref<SkStreamRewindable> bufferedStream(
SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER));
SkASSERT(bufferedStream.get() != NULL);
- // for now we don't allow purgeable with java inputstreams
- bitmap = doDecode(env, bufferedStream, padding, options, false, false);
+ bitmap = doDecode(env, bufferedStream, padding, options);
}
return bitmap;
}
@@ -542,76 +494,33 @@
SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file,
SkFILEStream::kCallerRetains_Ownership));
- SkAutoTUnref<SkStreamRewindable> stream;
+ // Use a buffered stream. Although an SkFILEStream can be rewound, this
+ // ensures that SkImageDecoder::Factory never rewinds beyond the
+ // current position of the file descriptor.
+ SkAutoTUnref<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream,
+ BYTES_TO_BUFFER));
- // Retain the old behavior of allowing purgeable if both purgeable and
- // shareable are set to true.
- bool isPurgeable = optionsPurgeable(env, bitmapFactoryOptions)
- && optionsShareable(env, bitmapFactoryOptions);
- if (isPurgeable) {
- // Copy the stream, so the image can be decoded multiple times without
- // continuing to modify the original file descriptor.
- // Copy beginning from the current position.
- const size_t fileSize = fileStream->getLength() - fileStream->getPosition();
- void* buffer = sk_malloc_flags(fileSize, 0);
- if (buffer == NULL) {
- return nullObjectReturn("Could not make a copy for ashmem");
- }
-
- SkAutoTUnref<SkData> data(SkData::NewFromMalloc(buffer, fileSize));
-
- if (fileStream->read(buffer, fileSize) != fileSize) {
- return nullObjectReturn("Could not read the file.");
- }
-
- stream.reset(new SkMemoryStream(data));
- } else {
- // Use a buffered stream. Although an SkFILEStream can be rewound, this
- // ensures that SkImageDecoder::Factory never rewinds beyond the
- // current position of the file descriptor.
- stream.reset(SkFrontBufferedStream::Create(fileStream, BYTES_TO_BUFFER));
- }
-
- return doDecode(env, stream, padding, bitmapFactoryOptions, isPurgeable);
+ return doDecode(env, stream, padding, bitmapFactoryOptions);
}
static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jlong native_asset,
jobject padding, jobject options) {
- SkStreamRewindable* stream;
Asset* asset = reinterpret_cast<Asset*>(native_asset);
- bool forcePurgeable = optionsPurgeable(env, options);
- if (forcePurgeable) {
- // if we could "ref/reopen" the asset, we may not need to copy it here
- // and we could assume optionsShareable, since assets are always RO
- stream = CopyAssetToStream(asset);
- if (stream == NULL) {
- return NULL;
- }
- } else {
- // since we know we'll be done with the asset when we return, we can
- // just use a simple wrapper
- stream = new AssetStreamAdaptor(asset,
- AssetStreamAdaptor::kNo_OwnAsset,
- AssetStreamAdaptor::kNo_HasMemoryBase);
- }
- SkAutoUnref aur(stream);
- return doDecode(env, stream, padding, options, forcePurgeable, forcePurgeable);
+ // since we know we'll be done with the asset when we return, we can
+ // just use a simple wrapper
+ SkAutoTUnref<SkStreamRewindable> stream(new AssetStreamAdaptor(asset,
+ AssetStreamAdaptor::kNo_OwnAsset, AssetStreamAdaptor::kNo_HasMemoryBase));
+ return doDecode(env, stream, padding, options);
}
static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray,
jint offset, jint length, jobject options) {
- /* If optionsShareable() we could decide to just wrap the java array and
- share it, but that means adding a globalref to the java array object
- and managing its lifetime. For now we just always copy the array's data
- if optionsPurgeable(), unless we're just decoding bounds.
- */
- bool purgeable = optionsPurgeable(env, options) && !optionsJustBounds(env, options);
AutoJavaByteArray ar(env, byteArray);
- SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, purgeable);
+ SkMemoryStream* stream = new SkMemoryStream(ar.ptr() + offset, length, false);
SkAutoUnref aur(stream);
- return doDecode(env, stream, NULL, options, purgeable);
+ return doDecode(env, stream, NULL, options);
}
static void nativeRequestCancel(JNIEnv*, jobject joptions) {
@@ -675,8 +584,6 @@
gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z");
gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
- gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
- gOptions_shareableFieldID = getFieldIDCheck(env, options_class, "inInputShareable", "Z");
gOptions_preferQualityOverSpeedFieldID = getFieldIDCheck(env, options_class,
"inPreferQualityOverSpeed", "Z");
gOptions_scaledFieldID = getFieldIDCheck(env, options_class, "inScaled", "Z");
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 5fca582..6de3b9e 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -19,12 +19,14 @@
#include <android_runtime/AndroidRuntime.h>
#include "SkCanvas.h"
+#include "SkClipStack.h"
#include "SkDevice.h"
+#include "SkDeque.h"
#include "SkDrawFilter.h"
#include "SkGraphics.h"
-#include "SkImageRef_GlobalPool.h"
#include "SkPorterDuff.h"
#include "SkShader.h"
+#include "SkTArray.h"
#include "SkTemplates.h"
#ifdef USE_MINIKIN
@@ -44,25 +46,6 @@
namespace android {
-// Holds an SkCanvas reference plus additional native data.
-class NativeCanvasWrapper {
-public:
- NativeCanvasWrapper(SkCanvas* canvas)
- : mCanvas(canvas) { }
-
- SkCanvas* getCanvas() const {
- return mCanvas.get();
- }
-
- void setCanvas(SkCanvas* canvas) {
- SkASSERT(canvas);
- mCanvas.reset(canvas);
- }
-
-private:
- SkAutoTUnref<SkCanvas> mCanvas;
-};
-
class ClipCopier : public SkCanvas::ClipVisitor {
public:
ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
@@ -81,6 +64,155 @@
SkCanvas* m_dstCanvas;
};
+// Holds an SkCanvas reference plus additional native data.
+class NativeCanvasWrapper {
+private:
+ struct SaveRec {
+ int saveCount;
+ SkCanvas::SaveFlags saveFlags;
+ };
+
+public:
+ NativeCanvasWrapper(SkCanvas* canvas)
+ : mCanvas(canvas)
+ , mSaveStack(NULL) {
+ SkASSERT(canvas);
+ }
+
+ ~NativeCanvasWrapper() {
+ delete mSaveStack;
+ }
+
+ SkCanvas* getCanvas() const {
+ return mCanvas.get();
+ }
+
+ void setCanvas(SkCanvas* canvas) {
+ SkASSERT(canvas);
+ mCanvas.reset(canvas);
+
+ delete mSaveStack;
+ mSaveStack = NULL;
+ }
+
+ int save(SkCanvas::SaveFlags flags) {
+ int count = mCanvas->save();
+ recordPartialSave(flags);
+ return count;
+ }
+
+ int saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SkCanvas::SaveFlags flags) {
+ int count = mCanvas->saveLayer(bounds, paint,
+ static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
+ recordPartialSave(flags);
+ return count;
+ }
+
+ int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+ SkCanvas::SaveFlags flags) {
+ int count = mCanvas->saveLayerAlpha(bounds, alpha,
+ static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag));
+ recordPartialSave(flags);
+ return count;
+ }
+
+ void restore() {
+ const SaveRec* rec = (NULL == mSaveStack)
+ ? NULL
+ : static_cast<SaveRec*>(mSaveStack->back());
+ int currentSaveCount = mCanvas->getSaveCount() - 1;
+ SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount);
+
+ if (NULL == rec || rec->saveCount != currentSaveCount) {
+ // Fast path - no record for this frame.
+ mCanvas->restore();
+ return;
+ }
+
+ bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag);
+ bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag);
+
+ SkMatrix savedMatrix;
+ if (preserveMatrix) {
+ savedMatrix = mCanvas->getTotalMatrix();
+ }
+
+ SkTArray<SkClipStack::Element> savedClips;
+ if (preserveClip) {
+ saveClipsForFrame(savedClips, currentSaveCount);
+ }
+
+ mCanvas->restore();
+
+ if (preserveMatrix) {
+ mCanvas->setMatrix(savedMatrix);
+ }
+
+ if (preserveClip && !savedClips.empty()) {
+ applyClips(savedClips);
+ }
+
+ mSaveStack->pop_back();
+ }
+
+private:
+ void recordPartialSave(SkCanvas::SaveFlags flags) {
+ // A partial save is a save operation which doesn't capture the full canvas state.
+ // (either kMatrix_SaveFlags or kClip_SaveFlag is missing).
+
+ // Mask-out non canvas state bits.
+ flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag);
+
+ if (SkCanvas::kMatrixClip_SaveFlag == flags) {
+ // not a partial save.
+ return;
+ }
+
+ if (NULL == mSaveStack) {
+ mSaveStack = new SkDeque(sizeof(struct SaveRec), 8);
+ }
+
+ SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back());
+ // Store the save counter in the SkClipStack domain.
+ // (0-based, equal to the number of save ops on the stack).
+ rec->saveCount = mCanvas->getSaveCount() - 1;
+ rec->saveFlags = flags;
+ }
+
+ void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips,
+ int frameSaveCount) {
+ SkClipStack::Iter clipIterator(*mCanvas->getClipStack(),
+ SkClipStack::Iter::kTop_IterStart);
+ while (const SkClipStack::Element* elem = clipIterator.next()) {
+ if (elem->getSaveCount() < frameSaveCount) {
+ // done with the current frame.
+ break;
+ }
+ SkASSERT(elem->getSaveCount() == frameSaveCount);
+ clips.push_back(*elem);
+ }
+ }
+
+ void applyClips(const SkTArray<SkClipStack::Element>& clips) {
+ ClipCopier clipCopier(mCanvas);
+
+ // The clip stack stores clips in device space.
+ SkMatrix origMatrix = mCanvas->getTotalMatrix();
+ mCanvas->resetMatrix();
+
+ // We pushed the clips in reverse order.
+ for (int i = clips.count() - 1; i >= 0; --i) {
+ clips[i].replay(&clipCopier);
+ }
+
+ mCanvas->setMatrix(origMatrix);
+ }
+
+ SkAutoTUnref<SkCanvas> mCanvas;
+ SkDeque* mSaveStack; // lazily allocated, tracks partial saves.
+};
+
// Returns true if the SkCanvas's clip is non-empty.
static jboolean hasNonEmptyClip(const SkCanvas& canvas) {
bool emptyClip = canvas.isClipEmpty();
@@ -89,11 +221,15 @@
class SkCanvasGlue {
public:
+ // Get the native wrapper for a given handle.
+ static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) {
+ SkASSERT(nativeHandle);
+ return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+ }
// Get the SkCanvas for a given native handle.
static inline SkCanvas* getNativeCanvas(jlong nativeHandle) {
- SkASSERT(nativeHandle);
- NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle);
+ NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle);
SkCanvas* canvas = wrapper->getCanvas();
SkASSERT(canvas);
@@ -161,8 +297,6 @@
}
static void freeCaches(JNIEnv* env, jobject) {
- // these are called in no particular order
- SkImageRef_GlobalPool::SetRAMUsed(0);
SkGraphics::PurgeFontCache();
}
@@ -170,134 +304,95 @@
TextLayoutEngine::getInstance().purgeCaches();
}
- static jboolean isOpaque(JNIEnv* env, jobject jcanvas) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
+ static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
bool result = canvas->getDevice()->accessBitmap(false).isOpaque();
return result ? JNI_TRUE : JNI_FALSE;
}
- static jint getWidth(JNIEnv* env, jobject jcanvas) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
+ static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
int width = canvas->getDevice()->accessBitmap(false).width();
return static_cast<jint>(width);
}
- static jint getHeight(JNIEnv* env, jobject jcanvas) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
+ static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
int height = canvas->getDevice()->accessBitmap(false).height();
return static_cast<jint>(height);
}
- static jint saveAll(JNIEnv* env, jobject jcanvas) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- int result = GraphicsJNI::getNativeCanvas(env, jcanvas)->save();
- return static_cast<jint>(result);
- }
-
- static jint save(JNIEnv* env, jobject jcanvas, jint flagsHandle) {
+ static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- int result = GraphicsJNI::getNativeCanvas(env, jcanvas)->save(flags);
- return static_cast<jint>(result);
+ return static_cast<jint>(wrapper->save(flags));
}
- static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds,
- jlong paintHandle, jint flags) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloat l, jfloat t, jfloat r, jfloat b,
+ jlong paintHandle, jint flagsHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect* bounds_ = NULL;
- SkRect storage;
- if (bounds != NULL) {
- GraphicsJNI::jrectf_to_rect(env, bounds, &storage);
- bounds_ = &storage;
- }
- return canvas->saveLayer(bounds_, paint, static_cast<SkCanvas::SaveFlags>(flags));
- }
-
- static jint saveLayer4F(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat l, jfloat t, jfloat r, jfloat b,
- jlong paintHandle, jint flags) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
SkRect bounds;
bounds.set(l, t, r, b);
- int result = canvas->saveLayer(&bounds, paint,
- static_cast<SkCanvas::SaveFlags>(flags));
- return static_cast<jint>(result);
+ return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags));
}
static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle,
- jobject bounds, jint alpha, jint flags) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkRect* bounds_ = NULL;
- SkRect storage;
- if (bounds != NULL) {
- GraphicsJNI::jrectf_to_rect(env, bounds, &storage);
- bounds_ = &storage;
- }
- int result = canvas->saveLayerAlpha(bounds_, alpha,
- static_cast<SkCanvas::SaveFlags>(flags));
- return static_cast<jint>(result);
- }
-
- static jint saveLayerAlpha4F(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat l, jfloat t, jfloat r, jfloat b,
- jint alpha, jint flags) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ jfloat l, jfloat t, jfloat r, jfloat b,
+ jint alpha, jint flagsHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+ SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
SkRect bounds;
bounds.set(l, t, r, b);
- int result = canvas->saveLayerAlpha(&bounds, alpha,
- static_cast<SkCanvas::SaveFlags>(flags));
- return static_cast<jint>(result);
+ return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags));
}
- static void restore(JNIEnv* env, jobject jcanvas) {
- NPE_CHECK_RETURN_VOID(env, jcanvas);
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
- if (canvas->getSaveCount() <= 1) { // cannot restore anymore
+ static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
+ if (wrapper->getCanvas()->getSaveCount() <= 1) { // cannot restore anymore
doThrowISE(env, "Underflow in restore");
return;
}
- canvas->restore();
+ wrapper->restore();
}
- static jint getSaveCount(JNIEnv* env, jobject jcanvas) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- int result = GraphicsJNI::getNativeCanvas(env, jcanvas)->getSaveCount();
- return static_cast<jint>(result);
+ static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
+ return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount());
}
- static void restoreToCount(JNIEnv* env, jobject jcanvas, jint restoreCount) {
- NPE_CHECK_RETURN_VOID(env, jcanvas);
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
+ static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle,
+ jint restoreCount) {
+ NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle);
if (restoreCount < 1) {
doThrowIAE(env, "Underflow in restoreToCount");
return;
}
- canvas->restoreToCount(restoreCount);
+
+ while (wrapper->getCanvas()->getSaveCount() > restoreCount) {
+ wrapper->restore();
+ }
}
- static void translate(JNIEnv* env, jobject jcanvas, jfloat dx, jfloat dy) {
- NPE_CHECK_RETURN_VOID(env, jcanvas);
- (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->translate(dx, dy);
+ static void translate(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat dx, jfloat dy) {
+ getNativeCanvas(canvasHandle)->translate(dx, dy);
}
- static void scale__FF(JNIEnv* env, jobject jcanvas, jfloat sx, jfloat sy) {
- NPE_CHECK_RETURN_VOID(env, jcanvas);
- (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->scale(sx, sy);
+ static void scale__FF(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat sx, jfloat sy) {
+ getNativeCanvas(canvasHandle)->scale(sx, sy);
}
- static void rotate__F(JNIEnv* env, jobject jcanvas, jfloat degrees) {
- NPE_CHECK_RETURN_VOID(env, jcanvas);
- (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->rotate(degrees);
+ static void rotate__F(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat degrees) {
+ getNativeCanvas(canvasHandle)->rotate(degrees);
}
- static void skew__FF(JNIEnv* env, jobject jcanvas, jfloat sx, jfloat sy) {
- NPE_CHECK_RETURN_VOID(env, jcanvas);
- (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->skew(sx, sy);
+ static void skew__FF(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat sx, jfloat sy) {
+ getNativeCanvas(canvasHandle)->skew(sx, sy);
}
static void concat(JNIEnv* env, jobject, jlong canvasHandle,
@@ -318,56 +413,16 @@
}
}
- static jboolean clipRect_FFFF(JNIEnv* env, jobject jcanvas, jfloat left,
- jfloat top, jfloat right, jfloat bottom) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
+ static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle,
+ jfloat left, jfloat top, jfloat right,
+ jfloat bottom, jint op) {
SkRect r;
r.set(left, top, right, bottom);
- SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
- c->clipRect(r);
+ SkCanvas* c = getNativeCanvas(canvasHandle);
+ c->clipRect(r, static_cast<SkRegion::Op>(op));
return hasNonEmptyClip(*c);
}
- static jboolean clipRect_IIII(JNIEnv* env, jobject jcanvas, jint left,
- jint top, jint right, jint bottom) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- SkRect r;
- r.set(SkIntToScalar(left), SkIntToScalar(top),
- SkIntToScalar(right), SkIntToScalar(bottom));
- SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
- c->clipRect(r);
- return hasNonEmptyClip(*c);
- }
-
- static jboolean clipRect_RectF(JNIEnv* env, jobject jcanvas, jobject rectf) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- NPE_CHECK_RETURN_ZERO(env, rectf);
- SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
- SkRect tmp;
- c->clipRect(*GraphicsJNI::jrectf_to_rect(env, rectf, &tmp));
- return hasNonEmptyClip(*c);
- }
-
- static jboolean clipRect_Rect(JNIEnv* env, jobject jcanvas, jobject rect) {
- NPE_CHECK_RETURN_ZERO(env, jcanvas);
- NPE_CHECK_RETURN_ZERO(env, rect);
- SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
- SkRect tmp;
- c->clipRect(*GraphicsJNI::jrect_to_rect(env, rect, &tmp));
- return hasNonEmptyClip(*c);
-
- }
-
- static jboolean clipRect(JNIEnv* env, jobject, jlong canvasHandle,
- jfloat left, jfloat top, jfloat right, jfloat bottom,
- jint op) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkRect rect;
- rect.set(left, top, right, bottom);
- canvas->clipRect(rect, static_cast<SkRegion::Op>(op));
- return hasNonEmptyClip(*canvas);
- }
-
static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle,
jlong pathHandle, jint op) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
@@ -380,7 +435,16 @@
jlong deviceRgnHandle, jint op) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
- canvas->clipRegion(*deviceRgn, static_cast<SkRegion::Op>(op));
+ SkPath rgnPath;
+ if (deviceRgn->getBoundaryPath(&rgnPath)) {
+ // The region is specified in device space.
+ SkMatrix savedMatrix = canvas->getTotalMatrix();
+ canvas->resetMatrix();
+ canvas->clipPath(rgnPath, static_cast<SkRegion::Op>(op));
+ canvas->setMatrix(savedMatrix);
+ } else {
+ canvas->clipRect(SkRect::MakeEmpty(), static_cast<SkRegion::Op>(op));
+ }
return hasNonEmptyClip(*canvas);
}
@@ -390,15 +454,6 @@
canvas->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
}
- static jboolean quickReject__RectF(JNIEnv* env, jobject, jlong canvasHandle,
- jobject rect) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkRect rect_;
- GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
- bool result = canvas->quickReject(rect_);
- return result ? JNI_TRUE : JNI_FALSE;
- }
-
static jboolean quickReject__Path(JNIEnv* env, jobject, jlong canvasHandle,
jlong pathHandle) {
SkCanvas* canvas = getNativeCanvas(canvasHandle);
@@ -448,15 +503,13 @@
canvas->drawPaint(*paint);
}
- static void doPoints(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
- jint offset, jint count, jobject jpaint,
- jint modeHandle) {
- NPE_CHECK_RETURN_VOID(env, jcanvas);
+ static void doPoints(JNIEnv* env, jlong canvasHandle,
+ jfloatArray jptsArray, jint offset, jint count,
+ jlong paintHandle, jint modeHandle) {
NPE_CHECK_RETURN_VOID(env, jptsArray);
- NPE_CHECK_RETURN_VOID(env, jpaint);
SkCanvas::PointMode mode = static_cast<SkCanvas::PointMode>(modeHandle);
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
- const SkPaint& paint = *GraphicsJNI::getNativePaint(env, jpaint);
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
AutoJavaFloatArray autoPts(env, jptsArray);
float* floats = autoPts.ptr();
@@ -476,29 +529,28 @@
pts[i].set(src[0], src[1]);
src += 2;
}
- canvas->drawPoints(mode, count, pts, paint);
+ canvas->drawPoints(mode, count, pts, *paint);
}
- static void drawPoints(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
- jint offset, jint count, jobject jpaint) {
- doPoints(env, jcanvas, jptsArray, offset, count, jpaint,
+ static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloatArray jptsArray, jint offset,
+ jint count, jlong paintHandle) {
+ doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
SkCanvas::kPoints_PointMode);
}
- static void drawLines(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
- jint offset, jint count, jobject jpaint) {
- doPoints(env, jcanvas, jptsArray, offset, count, jpaint,
+ static void drawLines(JNIEnv* env, jobject, jlong canvasHandle,
+ jfloatArray jptsArray, jint offset, jint count,
+ jlong paintHandle) {
+ doPoints(env, canvasHandle, jptsArray, offset, count, paintHandle,
SkCanvas::kLines_PointMode);
}
- static void drawPoint(JNIEnv* env, jobject jcanvas, jfloat x, jfloat y,
- jobject jpaint) {
- NPE_CHECK_RETURN_VOID(env, jcanvas);
- NPE_CHECK_RETURN_VOID(env, jpaint);
- SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
- const SkPaint& paint = *GraphicsJNI::getNativePaint(env, jpaint);
-
- canvas->drawPoint(x, y, paint);
+ static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
+ jlong paintHandle) {
+ SkCanvas* canvas = getNativeCanvas(canvasHandle);
+ const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ canvas->drawPoint(x, y, *paint);
}
static void drawLine__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
@@ -509,15 +561,6 @@
canvas->drawLine(startX, startY, stopX, stopY, *paint);
}
- static void drawRect__RectFPaint(JNIEnv* env, jobject, jlong canvasHandle,
- jobject rect, jlong paintHandle) {
- SkCanvas* canvas = getNativeCanvas(canvasHandle);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkRect rect_;
- GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
- canvas->drawRect(rect_, *paint);
- }
-
static void drawRect__FFFFPaint(JNIEnv* env, jobject, jlong canvasHandle,
jfloat left, jfloat top, jfloat right,
jfloat bottom, jlong paintHandle) {
@@ -863,32 +906,44 @@
}
#ifdef USE_MINIKIN
- static void drawGlyphsToSkia(SkCanvas* canvas, SkPaint* paint, Layout* layout, float x, float y) {
- size_t nGlyphs = layout->nGlyphs();
- uint16_t *glyphs = new uint16_t[nGlyphs];
- SkPoint *pos = new SkPoint[nGlyphs];
- SkTypeface *lastFace = NULL;
- SkTypeface *skFace = NULL;
- size_t start = 0;
+ class DrawTextFunctor {
+ public:
+ DrawTextFunctor(const Layout& layout, SkCanvas* canvas, jfloat x, jfloat y, SkPaint* paint,
+ uint16_t* glyphs, SkPoint* pos)
+ : layout(layout), canvas(canvas), x(x), y(y), paint(paint), glyphs(glyphs),
+ pos(pos) { }
- paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- for (size_t i = 0; i < nGlyphs; i++) {
- MinikinFontSkia *mfs = static_cast<MinikinFontSkia *>(layout->getFont(i));
- skFace = mfs->GetSkTypeface();
- glyphs[i] = layout->getGlyphId(i);
- pos[i].fX = x + layout->getX(i);
- pos[i].fY = y + layout->getY(i);
- if (i > 0 && skFace != lastFace) {
- paint->setTypeface(lastFace);
- canvas->drawPosText(glyphs + start, (i - start) << 1, pos + start, *paint);
- start = i;
+ void operator()(size_t start, size_t end) {
+ for (size_t i = start; i < end; i++) {
+ glyphs[i] = layout.getGlyphId(i);
+ pos[i].fX = x + layout.getX(i);
+ pos[i].fY = y + layout.getY(i);
}
- lastFace = skFace;
+ canvas->drawPosText(glyphs + start, (end - start) << 1, pos + start, *paint);
}
- if (skFace != NULL) {
- paint->setTypeface(skFace);
- canvas->drawPosText(glyphs + start, (nGlyphs - start) << 1, pos + start, *paint);
- }
+ private:
+ const Layout& layout;
+ SkCanvas* canvas;
+ jfloat x;
+ jfloat y;
+ SkPaint* paint;
+ uint16_t* glyphs;
+ SkPoint* pos;
+ };
+
+ static void drawGlyphsToSkia(SkCanvas* canvas, SkPaint* paint, const Layout& layout, float x, float y) {
+ size_t nGlyphs = layout.nGlyphs();
+ uint16_t* glyphs = new uint16_t[nGlyphs];
+ SkPoint* pos = new SkPoint[nGlyphs];
+
+ x += MinikinUtils::xOffsetForTextAlign(paint, layout);
+ SkPaint::Align align = paint->getTextAlign();
+ paint->setTextAlign(SkPaint::kLeft_Align);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ DrawTextFunctor f(layout, canvas, x, y, paint, glyphs, pos);
+ MinikinUtils::forFontRun(layout, paint, f);
+ doDrawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
+ paint->setTextAlign(align);
delete[] glyphs;
delete[] pos;
}
@@ -908,9 +963,9 @@
#ifdef USE_MINIKIN
Layout layout;
- MinikinUtils::SetLayoutProperties(&layout, paint, flags, typeface);
- layout.doLayout(textArray + start, count);
- drawGlyphsToSkia(canvas, paint, &layout, x, y);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface);
+ layout.doLayout(textArray, start, count, contextCount, css);
+ drawGlyphsToSkia(canvas, paint, layout, x, y);
#else
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
textArray, start, count, contextCount, flags);
@@ -1153,33 +1208,21 @@
{"initRaster", "(J)J", (void*) SkCanvasGlue::initRaster},
{"initCanvas", "(J)J", (void*) SkCanvasGlue::initCanvas},
{"native_setBitmap", "(JJZ)V", (void*) SkCanvasGlue::setBitmap},
- {"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
- {"getWidth","()I", (void*) SkCanvasGlue::getWidth},
- {"getHeight","()I", (void*) SkCanvasGlue::getHeight},
- {"save","()I", (void*) SkCanvasGlue::saveAll},
- {"save","(I)I", (void*) SkCanvasGlue::save},
- {"native_saveLayer","(JLandroid/graphics/RectF;JI)I",
- (void*) SkCanvasGlue::saveLayer},
- {"native_saveLayer","(JFFFFJI)I", (void*) SkCanvasGlue::saveLayer4F},
- {"native_saveLayerAlpha","(JLandroid/graphics/RectF;II)I",
- (void*) SkCanvasGlue::saveLayerAlpha},
- {"native_saveLayerAlpha","(JFFFFII)I",
- (void*) SkCanvasGlue::saveLayerAlpha4F},
- {"restore","()V", (void*) SkCanvasGlue::restore},
- {"getSaveCount","()I", (void*) SkCanvasGlue::getSaveCount},
- {"restoreToCount","(I)V", (void*) SkCanvasGlue::restoreToCount},
- {"translate","(FF)V", (void*) SkCanvasGlue::translate},
- {"scale","(FF)V", (void*) SkCanvasGlue::scale__FF},
- {"rotate","(F)V", (void*) SkCanvasGlue::rotate__F},
- {"skew","(FF)V", (void*) SkCanvasGlue::skew__FF},
+ {"native_isOpaque","(J)Z", (void*) SkCanvasGlue::isOpaque},
+ {"native_getWidth","(J)I", (void*) SkCanvasGlue::getWidth},
+ {"native_getHeight","(J)I", (void*) SkCanvasGlue::getHeight},
+ {"native_save","(JI)I", (void*) SkCanvasGlue::save},
+ {"native_saveLayer","(JFFFFJI)I", (void*) SkCanvasGlue::saveLayer},
+ {"native_saveLayerAlpha","(JFFFFII)I", (void*) SkCanvasGlue::saveLayerAlpha},
+ {"native_restore","(J)V", (void*) SkCanvasGlue::restore},
+ {"native_getSaveCount","(J)I", (void*) SkCanvasGlue::getSaveCount},
+ {"native_restoreToCount","(JI)V", (void*) SkCanvasGlue::restoreToCount},
+ {"native_translate","(JFF)V", (void*) SkCanvasGlue::translate},
+ {"native_scale","(JFF)V", (void*) SkCanvasGlue::scale__FF},
+ {"native_rotate","(JF)V", (void*) SkCanvasGlue::rotate__F},
+ {"native_skew","(JFF)V", (void*) SkCanvasGlue::skew__FF},
{"native_concat","(JJ)V", (void*) SkCanvasGlue::concat},
{"native_setMatrix","(JJ)V", (void*) SkCanvasGlue::setMatrix},
- {"clipRect","(FFFF)Z", (void*) SkCanvasGlue::clipRect_FFFF},
- {"clipRect","(IIII)Z", (void*) SkCanvasGlue::clipRect_IIII},
- {"clipRect","(Landroid/graphics/RectF;)Z",
- (void*) SkCanvasGlue::clipRect_RectF},
- {"clipRect","(Landroid/graphics/Rect;)Z",
- (void*) SkCanvasGlue::clipRect_Rect},
{"native_clipRect","(JFFFFI)Z", (void*) SkCanvasGlue::clipRect},
{"native_clipPath","(JJI)Z", (void*) SkCanvasGlue::clipPath},
{"native_clipRegion","(JJI)Z", (void*) SkCanvasGlue::clipRegion},
@@ -1187,8 +1230,6 @@
{"native_getClipBounds","(JLandroid/graphics/Rect;)Z",
(void*) SkCanvasGlue::getClipBounds},
{"native_getCTM", "(JJ)V", (void*)SkCanvasGlue::getCTM},
- {"native_quickReject","(JLandroid/graphics/RectF;)Z",
- (void*) SkCanvasGlue::quickReject__RectF},
{"native_quickReject","(JJ)Z", (void*) SkCanvasGlue::quickReject__Path},
{"native_quickReject","(JFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
{"native_drawRGB","(JIII)V", (void*) SkCanvasGlue::drawRGB},
@@ -1196,15 +1237,10 @@
{"native_drawColor","(JI)V", (void*) SkCanvasGlue::drawColor__I},
{"native_drawColor","(JII)V", (void*) SkCanvasGlue::drawColor__II},
{"native_drawPaint","(JJ)V", (void*) SkCanvasGlue::drawPaint},
- {"drawPoint", "(FFLandroid/graphics/Paint;)V",
- (void*) SkCanvasGlue::drawPoint},
- {"drawPoints", "([FIILandroid/graphics/Paint;)V",
- (void*) SkCanvasGlue::drawPoints},
- {"drawLines", "([FIILandroid/graphics/Paint;)V",
- (void*) SkCanvasGlue::drawLines},
+ {"native_drawPoint", "(JFFJ)V", (void*) SkCanvasGlue::drawPoint},
+ {"native_drawPoints", "(J[FIIJ)V", (void*) SkCanvasGlue::drawPoints},
+ {"native_drawLines", "(J[FIIJ)V", (void*) SkCanvasGlue::drawLines},
{"native_drawLine","(JFFFFJ)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
- {"native_drawRect","(JLandroid/graphics/RectF;J)V",
- (void*) SkCanvasGlue::drawRect__RectFPaint},
{"native_drawRect","(JFFFFJ)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
{"native_drawOval","(JLandroid/graphics/RectF;J)V",
(void*) SkCanvasGlue::drawOval},
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 3bab8a2..dd6b36f 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -23,6 +23,9 @@
#include "GraphicsJNI.h"
#include <ScopedPrimitiveArray.h>
#include <ScopedUtfChars.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <androidfw/AssetManager.h>
+#include "Utils.h"
#ifdef USE_MINIKIN
#include <minikin/FontFamily.h>
@@ -51,19 +54,60 @@
#endif
}
+#ifdef USE_MINIKIN
+static jboolean addSkTypeface(FontFamily* family, SkTypeface* face) {
+ MinikinFont* minikinFont = new MinikinFontSkia(face);
+ bool result = family->addFont(minikinFont);
+ minikinFont->Unref();
+ return result;
+}
+#endif
+
static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path) {
#ifdef USE_MINIKIN
NPE_CHECK_RETURN_ZERO(env, path);
ScopedUtfChars str(env, path);
- ALOGD("addFont %s", str.c_str());
SkTypeface* face = SkTypeface::CreateFromFile(str.c_str());
if (face == NULL) {
ALOGE("addFont failed to create font %s", str.c_str());
return false;
}
- MinikinFont* minikinFont = new MinikinFontSkia(face);
FontFamily* fontFamily = (FontFamily*)familyPtr;
- return fontFamily->addFont(minikinFont);
+ return addSkTypeface(fontFamily, face);
+#else
+ return false;
+#endif
+}
+
+static jboolean FontFamily_addFontFromAsset(JNIEnv* env, jobject, jlong familyPtr,
+ jobject jassetMgr, jstring jpath) {
+#ifdef USE_MINIKIN
+ NPE_CHECK_RETURN_ZERO(env, jassetMgr);
+ NPE_CHECK_RETURN_ZERO(env, jpath);
+
+ AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
+ if (NULL == mgr) {
+ return false;
+ }
+
+ ScopedUtfChars str(env, jpath);
+ Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (NULL == asset) {
+ return false;
+ }
+
+ SkStream* stream = new AssetStreamAdaptor(asset,
+ AssetStreamAdaptor::kYes_OwnAsset,
+ AssetStreamAdaptor::kYes_HasMemoryBase);
+ SkTypeface* face = SkTypeface::CreateFromStream(stream);
+ // Note: SkTypeface::CreateFromStream holds its own reference to the stream
+ stream->unref();
+ if (face == NULL) {
+ ALOGE("addFontFromAsset failed to create font %s", str.c_str());
+ return false;
+ }
+ FontFamily* fontFamily = (FontFamily*)familyPtr;
+ return addSkTypeface(fontFamily, face);
#else
return false;
#endif
@@ -75,6 +119,8 @@
{ "nCreateFamily", "(Ljava/lang/String;I)J", (void*)FontFamily_create },
{ "nUnrefFamily", "(J)V", (void*)FontFamily_unref },
{ "nAddFont", "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
+ { "nAddFontFromAsset", "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
+ (void*)FontFamily_addFontFromAsset },
};
int register_android_graphics_FontFamily(JNIEnv* env)
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 64ad223..a4337ccc 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -3,6 +3,7 @@
#include "jni.h"
#include "JNIHelp.h"
#include "GraphicsJNI.h"
+#include "AndroidPicture.h"
#include "SkCanvas.h"
#include "SkDevice.h"
@@ -345,13 +346,13 @@
return p;
}
-SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
+AndroidPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
{
SkASSERT(env);
SkASSERT(picture);
SkASSERT(env->IsInstanceOf(picture, gPicture_class));
jlong pictureHandle = env->GetLongField(picture, gPicture_nativeInstanceID);
- SkPicture* p = reinterpret_cast<SkPicture*>(pictureHandle);
+ AndroidPicture* p = reinterpret_cast<AndroidPicture*>(pictureHandle);
SkASSERT(p);
return p;
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 73dd11b..2e2f920 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -14,7 +14,7 @@
class SkBitmapRegionDecoder;
class SkCanvas;
class SkPaint;
-class SkPicture;
+class AndroidPicture;
class GraphicsJNI {
public:
@@ -50,7 +50,7 @@
static SkPaint* getNativePaint(JNIEnv*, jobject paint);
static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
- static SkPicture* getNativePicture(JNIEnv*, jobject picture);
+ static AndroidPicture* getNativePicture(JNIEnv*, jobject picture);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
// Given the 'native' long held by the Rasterizer.java object, return a
diff --git a/core/jni/android/graphics/MinikinSkia.cpp b/core/jni/android/graphics/MinikinSkia.cpp
index 243fa10..25eb941 100644
--- a/core/jni/android/graphics/MinikinSkia.cpp
+++ b/core/jni/android/graphics/MinikinSkia.cpp
@@ -43,11 +43,14 @@
return !!glyph;
}
-static void MinikinFontSkia_SetSkiaPaint(SkTypeface* typeface, SkPaint* skPaint, const MinikinPaint& paint) {
- skPaint->setTypeface(typeface);
+static void MinikinFontSkia_SetSkiaPaint(const MinikinFont* font, SkPaint* skPaint, const MinikinPaint& paint) {
skPaint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- // TODO: set more paint parameters from Minikin
skPaint->setTextSize(paint.size);
+ skPaint->setTextScaleX(paint.scaleX);
+ skPaint->setTextSkewX(paint.skewX);
+ MinikinFontSkia::unpackPaintFlags(skPaint, paint.paintFlags);
+ // Apply font fakery on top of user-supplied flags.
+ MinikinFontSkia::populateSkPaint(skPaint, font, paint.fakery);
}
float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id,
@@ -55,7 +58,7 @@
SkPaint skPaint;
uint16_t glyph16 = glyph_id;
SkScalar skWidth;
- MinikinFontSkia_SetSkiaPaint(mTypeface, &skPaint, paint);
+ MinikinFontSkia_SetSkiaPaint(this, &skPaint, paint);
skPaint.getTextWidths(&glyph16, sizeof(glyph16), &skWidth, NULL);
#ifdef VERBOSE
ALOGD("width for typeface %d glyph %d = %f", mTypeface->uniqueID(), glyph_id, skWidth);
@@ -68,7 +71,7 @@
SkPaint skPaint;
uint16_t glyph16 = glyph_id;
SkRect skBounds;
- MinikinFontSkia_SetSkiaPaint(mTypeface, &skPaint, paint);
+ MinikinFontSkia_SetSkiaPaint(this, &skPaint, paint);
skPaint.getTextWidths(&glyph16, sizeof(glyph16), NULL, &skBounds);
bounds->mLeft = skBounds.fLeft;
bounds->mTop = skBounds.fTop;
@@ -88,7 +91,7 @@
}
}
-SkTypeface *MinikinFontSkia::GetSkTypeface() {
+SkTypeface *MinikinFontSkia::GetSkTypeface() const {
return mTypeface;
}
@@ -96,4 +99,29 @@
return mTypeface->uniqueID();
}
+uint32_t MinikinFontSkia::packPaintFlags(const SkPaint* paint) {
+ uint32_t flags = paint->getFlags();
+ SkPaint::Hinting hinting = paint->getHinting();
+ // select only flags that might affect text layout
+ flags &= (SkPaint::kAntiAlias_Flag | SkPaint::kFakeBoldText_Flag | SkPaint::kLinearText_Flag |
+ SkPaint::kSubpixelText_Flag | SkPaint::kDevKernText_Flag |
+ SkPaint::kEmbeddedBitmapText_Flag | SkPaint::kAutoHinting_Flag |
+ SkPaint::kVerticalText_Flag);
+ flags |= (hinting << 16);
+ return flags;
+}
+
+void MinikinFontSkia::unpackPaintFlags(SkPaint* paint, uint32_t paintFlags) {
+ paint->setFlags(paintFlags & SkPaint::kAllFlags);
+ paint->setHinting(static_cast<SkPaint::Hinting>(paintFlags >> 16));
+}
+
+void MinikinFontSkia::populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery) {
+ paint->setTypeface(reinterpret_cast<const MinikinFontSkia*>(font)->GetSkTypeface());
+ paint->setFakeBoldText(paint->isFakeBoldText() || fakery.isFakeBold());
+ if (fakery.isFakeItalic()) {
+ paint->setTextSkewX(paint->getTextSkewX() - 0.25f);
+ }
+}
+
}
diff --git a/core/jni/android/graphics/MinikinSkia.h b/core/jni/android/graphics/MinikinSkia.h
index 7a8954d..ac4d2a0 100644
--- a/core/jni/android/graphics/MinikinSkia.h
+++ b/core/jni/android/graphics/MinikinSkia.h
@@ -18,6 +18,7 @@
class MinikinFontSkia : public MinikinFont {
public:
+ // Note: this takes ownership of the reference (will unref on dtor)
explicit MinikinFontSkia(SkTypeface *typeface);
~MinikinFontSkia();
@@ -35,8 +36,13 @@
int32_t GetUniqueId() const;
- SkTypeface *GetSkTypeface();
+ SkTypeface* GetSkTypeface() const;
+ static uint32_t packPaintFlags(const SkPaint* paint);
+ static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags);
+
+ // set typeface and fake bold/italic parameters
+ static void populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery);
private:
SkTypeface *mTypeface;
};
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index 79381ad..a9360ea 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -14,32 +14,66 @@
* limitations under the License.
*/
+#define LOG_TAG "Minikin"
+#include <cutils/log.h>
+#include <string>
+
#include "SkPaint.h"
#include "minikin/Layout.h"
#include "TypefaceImpl.h"
+#include "MinikinSkia.h"
#include "MinikinUtils.h"
namespace android {
-void MinikinUtils::SetLayoutProperties(Layout* layout, SkPaint* paint, int flags,
- TypefaceImpl* typeface) {
+// Do an sprintf starting at offset n, abort on overflow
+static int snprintfcat(char* buf, int off, int size, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ int n = vsnprintf(buf + off, size - off, format, args);
+ LOG_ALWAYS_FATAL_IF(n >= size - off, "String overflow in setting layout properties");
+ va_end(args);
+ return off + n;
+}
+
+std::string MinikinUtils::setLayoutProperties(Layout* layout, const SkPaint* paint, int bidiFlags,
+ TypefaceImpl* typeface) {
TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
layout->setFontCollection(resolvedFace->fFontCollection);
FontStyle style = resolvedFace->fStyle;
char css[256];
- int off = snprintf(css, sizeof(css),
- "font-size: %d; font-weight: %d; font-style: %s; -minikin-bidi: %d;",
+ int off = snprintfcat(css, 0, sizeof(css),
+ "font-size: %d; font-scale-x: %f; font-skew-x: %f; -paint-flags: %d;"
+ " font-weight: %d; font-style: %s; -minikin-bidi: %d;",
(int)paint->getTextSize(),
+ paint->getTextScaleX(),
+ paint->getTextSkewX(),
+ MinikinFontSkia::packPaintFlags(paint),
style.getWeight() * 100,
style.getItalic() ? "italic" : "normal",
- flags);
+ bidiFlags);
SkString langString = paint->getPaintOptionsAndroid().getLanguage().getTag();
- off += snprintf(css + off, sizeof(css) - off, " lang: %s;", langString.c_str());
+ off = snprintfcat(css, off, sizeof(css), " lang: %s;", langString.c_str());
SkPaintOptionsAndroid::FontVariant var = paint->getPaintOptionsAndroid().getFontVariant();
const char* varstr = var == SkPaintOptionsAndroid::kElegant_Variant ? "elegant" : "compact";
- off += snprintf(css + off, sizeof(css) - off, " -minikin-variant: %s;", varstr);
+ off = snprintfcat(css, off, sizeof(css), " -minikin-variant: %s;", varstr);
layout->setProperties(css);
+ return std::string(css);
}
-}
\ No newline at end of file
+float MinikinUtils::xOffsetForTextAlign(SkPaint* paint, const Layout& layout) {
+ switch (paint->getTextAlign()) {
+ case SkPaint::kCenter_Align:
+ return layout.getAdvance() * -0.5f;
+ break;
+ case SkPaint::kRight_Align:
+ return -layout.getAdvance();
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+}
diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h
index 42f5e2f..a96c6b1 100644
--- a/core/jni/android/graphics/MinikinUtils.h
+++ b/core/jni/android/graphics/MinikinUtils.h
@@ -26,10 +26,42 @@
namespace android {
+class Layout;
+class TypefaceImpl;
+
class MinikinUtils {
public:
- static void SetLayoutProperties(Layout* layout, SkPaint* paint, int flags,
- TypefaceImpl* face);
+ static std::string setLayoutProperties(Layout* layout, const SkPaint* paint, int bidiFlags,
+ TypefaceImpl* typeface);
+
+ static float xOffsetForTextAlign(SkPaint* paint, const Layout& layout);
+
+ // f is a functor of type void f(size_t start, size_t end);
+ template <typename F>
+ static void forFontRun(const Layout& layout, SkPaint* paint, F& f) {
+ float saveSkewX = paint->getTextSkewX();
+ bool savefakeBold = paint->isFakeBoldText();
+ MinikinFont* curFont = NULL;
+ size_t start = 0;
+ size_t nGlyphs = layout.nGlyphs();
+ for (size_t i = 0; i < nGlyphs; i++) {
+ MinikinFont* nextFont = layout.getFont(i);
+ if (i > 0 && nextFont != curFont) {
+ MinikinFontSkia::populateSkPaint(paint, curFont, layout.getFakery(start));
+ f(start, i);
+ paint->setTextSkewX(saveSkewX);
+ paint->setFakeBoldText(savefakeBold);
+ start = i;
+ }
+ curFont = nextFont;
+ }
+ if (nGlyphs > start) {
+ MinikinFontSkia::populateSkPaint(paint, curFont, layout.getFakery(start));
+ f(start, nGlyphs);
+ paint->setTextSkewX(saveSkewX);
+ paint->setFakeBoldText(savefakeBold);
+ }
+ }
};
} // namespace android
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 8418162..8b11d31 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -429,27 +429,29 @@
GraphicsJNI::getNativePaint(env, paint)->setTextSkewX(skewX);
}
- static jfloat ascent(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- SkPaint::FontMetrics metrics;
- (void)GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
- return SkScalarToFloat(metrics.fAscent);
- }
-
- static jfloat descent(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- SkPaint::FontMetrics metrics;
- (void)GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
- return SkScalarToFloat(metrics.fDescent);
- }
-
- static SkScalar getMetricsInternal(SkPaint *paint, SkPaint::FontMetrics *metrics) {
+ static SkScalar getMetricsInternal(JNIEnv* env, jobject jpaint, SkPaint::FontMetrics *metrics) {
const int kElegantTop = 2500;
const int kElegantBottom = -1000;
- const int kElegantAscent = 1946;
- const int kElegantDescent = -512;
+ const int kElegantAscent = 1900;
+ const int kElegantDescent = -500;
const int kElegantLeading = 0;
+ SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
+#ifdef USE_MINIKIN
+ TypefaceImpl* typeface = GraphicsJNI::getNativeTypeface(env, jpaint);
+ typeface = TypefaceImpl_resolveDefault(typeface);
+ FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
+ float saveSkewX = paint->getTextSkewX();
+ bool savefakeBold = paint->isFakeBoldText();
+ MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
+#endif
SkScalar spacing = paint->getFontMetrics(metrics);
+#ifdef USE_MINIKIN
+ // The populateSkPaint call may have changed fake bold / text skew
+ // because we want to measure with those effects applied, so now
+ // restore the original settings.
+ paint->setTextSkewX(saveSkewX);
+ paint->setFakeBoldText(savefakeBold);
+#endif
SkPaintOptionsAndroid paintOpts = paint->getPaintOptionsAndroid();
if (paintOpts.getFontVariant() == SkPaintOptionsAndroid::kElegant_Variant) {
SkScalar size = paint->getTextSize();
@@ -463,10 +465,24 @@
return spacing;
}
+ static jfloat ascent(JNIEnv* env, jobject paint) {
+ NPE_CHECK_RETURN_ZERO(env, paint);
+ SkPaint::FontMetrics metrics;
+ getMetricsInternal(env, paint, &metrics);
+ return SkScalarToFloat(metrics.fAscent);
+ }
+
+ static jfloat descent(JNIEnv* env, jobject paint) {
+ NPE_CHECK_RETURN_ZERO(env, paint);
+ SkPaint::FontMetrics metrics;
+ getMetricsInternal(env, paint, &metrics);
+ return SkScalarToFloat(metrics.fDescent);
+ }
+
static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
NPE_CHECK_RETURN_ZERO(env, paint);
SkPaint::FontMetrics metrics;
- SkScalar spacing = getMetricsInternal(GraphicsJNI::getNativePaint(env, paint), &metrics);
+ SkScalar spacing = getMetricsInternal(env, paint, &metrics);
if (metricsObj) {
SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
@@ -483,7 +499,7 @@
NPE_CHECK_RETURN_ZERO(env, paint);
SkPaint::FontMetrics metrics;
- getMetricsInternal(GraphicsJNI::getNativePaint(env, paint), &metrics);
+ getMetricsInternal(env, paint, &metrics);
int ascent = SkScalarRoundToInt(metrics.fAscent);
int descent = SkScalarRoundToInt(metrics.fDescent);
int leading = SkScalarRoundToInt(metrics.fLeading);
@@ -520,8 +536,8 @@
#ifdef USE_MINIKIN
Layout layout;
TypefaceImpl* typeface = GraphicsJNI::getNativeTypeface(env, jpaint);
- MinikinUtils::SetLayoutProperties(&layout, paint, bidiFlags, typeface);
- layout.doLayout(textArray + index, count);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+ layout.doLayout(textArray, index, count, textLength, css);
result = layout.getAdvance();
#else
TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength,
@@ -554,8 +570,8 @@
#ifdef USE_MINIKIN
Layout layout;
TypefaceImpl* typeface = GraphicsJNI::getNativeTypeface(env, jpaint);
- MinikinUtils::SetLayoutProperties(&layout, paint, bidiFlags, typeface);
- layout.doLayout(textArray + start, count);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+ layout.doLayout(textArray, start, count, textLength, css);
width = layout.getAdvance();
#else
TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength,
@@ -582,8 +598,8 @@
#ifdef USE_MINIKIN
Layout layout;
TypefaceImpl* typeface = GraphicsJNI::getNativeTypeface(env, jpaint);
- MinikinUtils::SetLayoutProperties(&layout, paint, bidiFlags, typeface);
- layout.doLayout(textArray, textLength);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+ layout.doLayout(textArray, 0, textLength, textLength, css);
width = layout.getAdvance();
#else
TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength,
@@ -617,8 +633,8 @@
#ifdef USE_MINIKIN
Layout layout;
- MinikinUtils::SetLayoutProperties(&layout, paint, bidiFlags, typeface);
- layout.doLayout(text, count);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+ layout.doLayout(text, 0, count, count, css);
layout.getAdvances(widthsArray);
#else
TextLayout::getTextRunAdvances(paint, text, 0, count, count,
@@ -715,8 +731,8 @@
#ifdef USE_MINIKIN
Layout layout;
- MinikinUtils::SetLayoutProperties(&layout, paint, flags, typeface);
- layout.doLayout(text + start, count);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface);
+ layout.doLayout(text, start, count, contextCount, css);
layout.getAdvances(advancesArray);
totalAdvance = layout.getAdvance();
#else
@@ -822,26 +838,82 @@
return result;
}
- static void getTextPath(JNIEnv* env, SkPaint* paint, const jchar* text, jint count,
- jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
+#ifdef USE_MINIKIN
+ class GetTextFunctor {
+ public:
+ GetTextFunctor(const Layout& layout, SkPath* path, jfloat x, jfloat y, SkPaint* paint,
+ uint16_t* glyphs, SkPoint* pos)
+ : layout(layout), path(path), x(x), y(y), paint(paint), glyphs(glyphs), pos(pos) {
+ }
+
+ void operator()(size_t start, size_t end) {
+ for (size_t i = start; i < end; i++) {
+ glyphs[i] = layout.getGlyphId(i);
+ pos[i].fX = x + layout.getX(i);
+ pos[i].fY = y + layout.getY(i);
+ }
+ if (start == 0) {
+ paint->getPosTextPath(glyphs + start, (end - start) << 1, pos + start, path);
+ } else {
+ paint->getPosTextPath(glyphs + start, (end - start) << 1, pos + start, &tmpPath);
+ path->addPath(tmpPath);
+ }
+ }
+ private:
+ const Layout& layout;
+ SkPath* path;
+ jfloat x;
+ jfloat y;
+ SkPaint* paint;
+ uint16_t* glyphs;
+ SkPoint* pos;
+ SkPath tmpPath;
+ };
+#endif
+
+ static void getTextPath(JNIEnv* env, SkPaint* paint, TypefaceImpl* typeface, const jchar* text,
+ jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) {
+#ifdef USE_MINIKIN
+ Layout layout;
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+ layout.doLayout(text, 0, count, count, css);
+ size_t nGlyphs = layout.nGlyphs();
+ uint16_t* glyphs = new uint16_t[nGlyphs];
+ SkPoint* pos = new SkPoint[nGlyphs];
+
+ x += MinikinUtils::xOffsetForTextAlign(paint, layout);
+ SkPaint::Align align = paint->getTextAlign();
+ paint->setTextAlign(SkPaint::kLeft_Align);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
+ MinikinUtils::forFontRun(layout, paint, f);
+ paint->setTextAlign(align);
+ delete[] glyphs;
+ delete[] pos;
+#else
TextLayout::getTextPath(paint, text, count, bidiFlags, x, y, path);
+#endif
}
- static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags,
+ static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jlong typefaceHandle, jint bidiFlags,
jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) {
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
const jchar* textArray = env->GetCharArrayElements(text, NULL);
- getTextPath(env, paint, textArray + index, count, bidiFlags, x, y, path);
+ getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
}
- static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags,
+ static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle,
+ jlong typefaceHandle, jint bidiFlags,
jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) {
SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
- getTextPath(env, paint, textArray + start, end - start, bidiFlags, x, y, path);
+ getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path);
env->ReleaseStringChars(text, textArray);
}
@@ -928,34 +1000,48 @@
return count;
}
- static void doTextBounds(JNIEnv* env, const jchar* text, int count,
- jobject bounds, const SkPaint& paint, jint bidiFlags) {
+ static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
+ const SkPaint& paint, TypefaceImpl* typeface, jint bidiFlags) {
SkRect r;
SkIRect ir;
+#ifdef USE_MINIKIN
+ Layout layout;
+ std::string css = MinikinUtils::setLayoutProperties(&layout, &paint, bidiFlags, typeface);
+ layout.doLayout(text, 0, count, count, css);
+ MinikinRect rect;
+ layout.getBounds(&rect);
+ r.fLeft = rect.mLeft;
+ r.fTop = rect.mTop;
+ r.fRight = rect.mRight;
+ r.fBottom = rect.mBottom;
+#else
sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
text, 0, count, count, bidiFlags);
if (value == NULL) {
return;
}
paint.measureText(value->getGlyphs(), value->getGlyphsCount() << 1, &r);
+#endif
r.roundOut(&ir);
GraphicsJNI::irect_to_jrect(ir, env, bounds);
}
- static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle,
+ static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);;
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
- doTextBounds(env, textArray + start, end - start, bounds, *paint, bidiFlags);
+ doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
env->ReleaseStringChars(text, textArray);
}
- static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle,
+ static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
jcharArray text, jint index, jint count, jint bidiFlags, jobject bounds) {
const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetCharArrayElements(text, NULL);
- doTextBounds(env, textArray + index, count, bounds, *paint, bidiFlags);
+ doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
JNI_ABORT);
}
@@ -1035,11 +1121,11 @@
{"native_getTextRunCursor", "(J[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
{"native_getTextRunCursor", "(JLjava/lang/String;IIIII)I",
(void*) SkPaintGlue::getTextRunCursor__String},
- {"native_getTextPath","(JI[CIIFFJ)V", (void*) SkPaintGlue::getTextPath___C},
- {"native_getTextPath","(JILjava/lang/String;IIFFJ)V", (void*) SkPaintGlue::getTextPath__String},
- {"nativeGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V",
+ {"native_getTextPath","(JJI[CIIFFJ)V", (void*) SkPaintGlue::getTextPath___C},
+ {"native_getTextPath","(JJILjava/lang/String;IIFFJ)V", (void*) SkPaintGlue::getTextPath__String},
+ {"nativeGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
(void*) SkPaintGlue::getStringBounds },
- {"nativeGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V",
+ {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
(void*) SkPaintGlue::getCharArrayBounds },
{"native_setShadowLayer", "(JFFFI)V", (void*)SkPaintGlue::setShadowLayer},
{"native_hasShadowLayer", "(J)Z", (void*)SkPaintGlue::hasShadowLayer}
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index a8a3dae..0683f73 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -17,9 +17,9 @@
#include "jni.h"
#include "GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
+#include "AndroidPicture.h"
#include "SkCanvas.h"
-#include "SkPicture.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "CreateJavaOutputStreamAdaptor.h"
@@ -29,45 +29,41 @@
class SkPictureGlue {
public:
static jlong newPicture(JNIEnv* env, jobject, jlong srcHandle) {
- const SkPicture* src = reinterpret_cast<SkPicture*>(srcHandle);
- if (src) {
- return reinterpret_cast<jlong>(new SkPicture(*src));
- } else {
- return reinterpret_cast<jlong>(new SkPicture);
- }
+ const AndroidPicture* src = reinterpret_cast<AndroidPicture*>(srcHandle);
+ return reinterpret_cast<jlong>(new AndroidPicture(src));
}
static jlong deserialize(JNIEnv* env, jobject, jobject jstream,
jbyteArray jstorage) {
- SkPicture* picture = NULL;
+ AndroidPicture* picture = NULL;
SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
if (strm) {
- picture = SkPicture::CreateFromStream(strm);
+ picture = AndroidPicture::CreateFromStream(strm);
delete strm;
}
return reinterpret_cast<jlong>(picture);
}
static void killPicture(JNIEnv* env, jobject, jlong pictureHandle) {
- SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle);
+ AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
SkASSERT(picture);
- picture->unref();
+ delete picture;
}
static void draw(JNIEnv* env, jobject, jlong canvasHandle,
jlong pictureHandle) {
SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
- SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle);
+ AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
SkASSERT(canvas);
SkASSERT(picture);
picture->draw(canvas);
}
static jboolean serialize(JNIEnv* env, jobject, jlong pictureHandle,
- jobject jstream, jbyteArray jstorage) {
- SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle);
+ jobject jstream, jbyteArray jstorage) {
+ AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
-
+
if (NULL != strm) {
picture->serialize(strm);
delete strm;
@@ -78,19 +74,21 @@
static jint getWidth(JNIEnv* env, jobject jpic) {
NPE_CHECK_RETURN_ZERO(env, jpic);
- int width = GraphicsJNI::getNativePicture(env, jpic)->width();
+ AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic);
+ int width = pict->width();
return static_cast<jint>(width);
}
static jint getHeight(JNIEnv* env, jobject jpic) {
NPE_CHECK_RETURN_ZERO(env, jpic);
- int height = GraphicsJNI::getNativePicture(env, jpic)->height();
+ AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic);
+ int height = pict->height();
return static_cast<jint>(height);
}
static jlong beginRecording(JNIEnv* env, jobject, jlong pictHandle,
- jint w, jint h) {
- SkPicture* pict = reinterpret_cast<SkPicture*>(pictHandle);
+ jint w, jint h) {
+ AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
// beginRecording does not ref its return value, it just returns it.
SkCanvas* canvas = pict->beginRecording(w, h);
// the java side will wrap this guy in a Canvas.java, which will call
@@ -101,7 +99,7 @@
}
static void endRecording(JNIEnv* env, jobject, jlong pictHandle) {
- SkPicture* pict = reinterpret_cast<SkPicture*>(pictHandle);
+ AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
pict->endRecording();
}
};
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 9279758..495d08a 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "TextLayoutCache"
#include <utils/JenkinsHash.h>
+#include <utils/CallStack.h>
#include "TextLayoutCache.h"
#include "TextLayout.h"
@@ -89,6 +90,11 @@
sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) {
AutoMutex _l(mLock);
+#ifdef USE_MINIKIN
+ // We want to get rid of all legacy calls in the Minikin case, so log
+ ALOGW("TextLayoutCache being invoked!");
+ CallStack _cs(LOG_TAG);
+#endif
nsecs_t startTime = 0;
if (mDebugEnabled) {
startTime = systemTime(SYSTEM_TIME_MONOTONIC);
diff --git a/core/jni/android/graphics/TypefaceImpl.cpp b/core/jni/android/graphics/TypefaceImpl.cpp
index 958cd85..1800d0c 100644
--- a/core/jni/android/graphics/TypefaceImpl.cpp
+++ b/core/jni/android/graphics/TypefaceImpl.cpp
@@ -32,6 +32,7 @@
#include <minikin/FontCollection.h>
#include <minikin/FontFamily.h>
#include <minikin/Layout.h>
+#include "SkPaint.h"
#include "MinikinSkia.h"
#endif
@@ -134,18 +135,19 @@
return result;
}
+// Delete when removing USE_MINIKIN ifdef
TypefaceImpl* TypefaceImpl_createFromName(const char* name, SkTypeface::Style style) {
- // TODO: should create a font collection with all styles corresponding to
- // the name
SkTypeface* face = SkTypeface::CreateFromName(name, style);
return createFromSkTypeface(face);
}
+// Delete when removing USE_MINIKIN ifdef
TypefaceImpl* TypefaceImpl_createFromFile(const char* filename) {
SkTypeface* face = SkTypeface::CreateFromFile(filename);
return createFromSkTypeface(face);
}
+// Delete when removing USE_MINIKIN ifdef
TypefaceImpl* TypefaceImpl_createFromAsset(Asset* asset) {
SkStream* stream = new AssetStreamAdaptor(asset,
AssetStreamAdaptor::kYes_OwnAsset,
@@ -158,7 +160,6 @@
}
TypefaceImpl* TypefaceImpl_createFromFamilies(const jlong* families, size_t size) {
- ALOGD("createFromFamilies size=%d", size);
std::vector<FontFamily *>familyVec;
for (size_t i = 0; i < size; i++) {
FontFamily* family = reinterpret_cast<FontFamily*>(families[i]);
@@ -166,12 +167,25 @@
}
TypefaceImpl* result = new TypefaceImpl;
result->fFontCollection = new FontCollection(familyVec);
- result->fStyle = FontStyle(); // TODO: improve
+ if (size == 0) {
+ ALOGW("createFromFamilies creating empty collection");
+ result->fStyle = FontStyle();
+ } else {
+ const FontStyle defaultStyle;
+ FontFamily* firstFamily = reinterpret_cast<FontFamily*>(families[0]);
+ MinikinFont* mf = firstFamily->getClosestMatch(defaultStyle).font;
+ SkTypeface* skTypeface = reinterpret_cast<MinikinFontSkia*>(mf)->GetSkTypeface();
+ // TODO: probably better to query more precise style from family, will be important
+ // when we open up API to access 100..900 weights
+ result->fStyle = styleFromSkiaStyle(skTypeface->style());
+ }
return result;
}
void TypefaceImpl_unref(TypefaceImpl* face) {
- face->fFontCollection->Unref();
+ if (face != NULL) {
+ face->fFontCollection->Unref();
+ }
delete face;
}
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index d54aaa8..3812c27 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -24,6 +24,7 @@
#include "SkCanvas.h"
#include "SkDocument.h"
#include "SkPicture.h"
+#include "SkPictureRecorder.h"
#include "SkStream.h"
#include "SkRect.h"
@@ -32,15 +33,22 @@
struct PageRecord {
PageRecord(int width, int height, const SkRect& contentRect)
- : mPicture(new SkPicture()), mWidth(width), mHeight(height) {
+ : mPictureRecorder(new SkPictureRecorder())
+ , mPicture(NULL)
+ , mWidth(width)
+ , mHeight(height) {
mContentRect = contentRect;
}
~PageRecord() {
- mPicture->unref();
+ delete mPictureRecorder;
+ if (NULL != mPicture) {
+ mPicture->unref();
+ }
}
- SkPicture* const mPicture;
+ SkPictureRecorder* mPictureRecorder;
+ SkPicture* mPicture;
const int mWidth;
const int mHeight;
SkRect mContentRect;
@@ -62,8 +70,8 @@
mPages.push_back(page);
mCurrentPage = page;
- SkCanvas* canvas = page->mPicture->beginRecording(
- contentRect.width(), contentRect.height(), 0);
+ SkCanvas* canvas = page->mPictureRecorder->beginRecording(
+ contentRect.width(), contentRect.height(), NULL, 0);
// We pass this canvas to Java where it is used to construct
// a Java Canvas object which dereferences the pointer when it
@@ -75,7 +83,11 @@
void finishPage() {
assert(mCurrentPage != NULL);
- mCurrentPage->mPicture->endRecording();
+ assert(mCurrentPage->mPictureRecorder != NULL);
+ assert(mCurrentPage->mPicture == NULL);
+ mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->endRecording();
+ delete mCurrentPage->mPictureRecorder;
+ mCurrentPage->mPictureRecorder = NULL;
mCurrentPage = NULL;
}
@@ -89,7 +101,7 @@
canvas->clipRect(page->mContentRect);
canvas->translate(page->mContentRect.left(), page->mContentRect.top());
- canvas->drawPicture(*page->mPicture);
+ canvas->drawPicture(page->mPicture);
document->endPage();
}
@@ -97,11 +109,10 @@
}
void close() {
+ assert(NULL == mCurrentPage);
for (unsigned i = 0; i < mPages.size(); i++) {
delete mPages[i];
}
- delete mCurrentPage;
- mCurrentPage = NULL;
}
private:
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index ddcc396..c588942 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -52,7 +52,7 @@
jfieldID stringType;
jfieldID requiredPermission;
jfieldID maxDelay;
- jfieldID isWakeUpSensor;
+ jfieldID flags;
} gSensorOffsets;
@@ -81,7 +81,7 @@
sensorOffsets.requiredPermission = _env->GetFieldID(sensorClass, "mRequiredPermission",
"Ljava/lang/String;");
sensorOffsets.maxDelay = _env->GetFieldID(sensorClass, "mMaxDelay", "I");
- sensorOffsets.isWakeUpSensor = _env->GetFieldID(sensorClass, "mWakeUpSensor", "Z");
+ sensorOffsets.flags = _env->GetFieldID(sensorClass, "mFlags", "I");
}
static jint
@@ -117,7 +117,7 @@
env->SetObjectField(sensor, sensorOffsets.requiredPermission,
requiredPermission);
env->SetIntField(sensor, sensorOffsets.maxDelay, list->getMaxDelay());
- env->SetBooleanField(sensor, sensorOffsets.isWakeUpSensor, list->isWakeUpSensor());
+ env->SetIntField(sensor, sensorOffsets.flags, list->getFlags());
next++;
return size_t(next) < count ? next : 0;
}
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
new file mode 100644
index 0000000..69e991d
--- /dev/null
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -0,0 +1,661 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundTrigger-JNI"
+#include <utils/Log.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+#include <system/sound_trigger.h>
+#include <soundtrigger/SoundTriggerCallback.h>
+#include <soundtrigger/SoundTrigger.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+#include <binder/IMemory.h>
+#include <binder/MemoryDealer.h>
+
+using namespace android;
+
+static jclass gArrayListClass;
+static struct {
+ jmethodID add;
+} gArrayListMethods;
+
+static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger";
+static jclass gSoundTriggerClass;
+
+static const char* const kModuleClassPathName = "android/hardware/soundtrigger/SoundTriggerModule";
+static jclass gModuleClass;
+static struct {
+ jfieldID mNativeContext;
+ jfieldID mId;
+} gModuleFields;
+static jmethodID gPostEventFromNative;
+
+static const char* const kModulePropertiesClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$ModuleProperties";
+static jclass gModulePropertiesClass;
+static jmethodID gModulePropertiesCstor;
+
+static const char* const kSoundModelClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$SoundModel";
+static jclass gSoundModelClass;
+static struct {
+ jfieldID data;
+} gSoundModelFields;
+
+static const char* const kKeyPhraseClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$KeyPhrase";
+static jclass gKeyPhraseClass;
+static struct {
+ jfieldID recognitionModes;
+ jfieldID locale;
+ jfieldID text;
+ jfieldID numUsers;
+} gKeyPhraseFields;
+
+static const char* const kKeyPhraseSoundModelClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$KeyPhraseSoundModel";
+static jclass gKeyPhraseSoundModelClass;
+static struct {
+ jfieldID keyPhrases;
+} gKeyPhraseSoundModelFields;
+
+
+static const char* const kRecognitionEventClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$RecognitionEvent";
+static jclass gRecognitionEventClass;
+static jmethodID gRecognitionEventCstor;
+
+static const char* const kKeyPhraseRecognitionEventClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionEvent";
+static jclass gKeyPhraseRecognitionEventClass;
+static jmethodID gKeyPhraseRecognitionEventCstor;
+
+static const char* const kKeyPhraseRecognitionExtraClassPathName =
+ "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra";
+static jclass gKeyPhraseRecognitionExtraClass;
+static jmethodID gKeyPhraseRecognitionExtraCstor;
+
+static Mutex gLock;
+
+enum {
+ SOUNDTRIGGER_STATUS_OK = 0,
+ SOUNDTRIGGER_STATUS_ERROR = INT_MIN,
+ SOUNDTRIGGER_PERMISSION_DENIED = -1,
+ SOUNDTRIGGER_STATUS_NO_INIT = -19,
+ SOUNDTRIGGER_STATUS_BAD_VALUE = -22,
+ SOUNDTRIGGER_STATUS_DEAD_OBJECT = -32,
+ SOUNDTRIGGER_INVALID_OPERATION = -38,
+};
+
+enum {
+ SOUNDTRIGGER_EVENT_RECOGNITION = 1,
+ SOUNDTRIGGER_EVENT_SERVICE_DIED = 2,
+};
+
+// ----------------------------------------------------------------------------
+// ref-counted object for callbacks
+class JNISoundTriggerCallback: public SoundTriggerCallback
+{
+public:
+ JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
+ ~JNISoundTriggerCallback();
+
+ virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event);
+ virtual void onServiceDied();
+
+private:
+ jclass mClass; // Reference to SoundTrigger class
+ jobject mObject; // Weak ref to SoundTrigger Java object to call on
+};
+
+JNISoundTriggerCallback::JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
+{
+
+ // Hold onto the SoundTriggerModule class for use in calling the static method
+ // that posts events to the application thread.
+ jclass clazz = env->GetObjectClass(thiz);
+ if (clazz == NULL) {
+ ALOGE("Can't find class %s", kModuleClassPathName);
+ return;
+ }
+ mClass = (jclass)env->NewGlobalRef(clazz);
+
+ // We use a weak reference so the SoundTriggerModule object can be garbage collected.
+ // The reference is only used as a proxy for callbacks.
+ mObject = env->NewGlobalRef(weak_thiz);
+}
+
+JNISoundTriggerCallback::~JNISoundTriggerCallback()
+{
+ // remove global references
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->DeleteGlobalRef(mObject);
+ env->DeleteGlobalRef(mClass);
+}
+
+void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognition_event *event)
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ jobject jEvent;
+
+ jbyteArray jData = NULL;
+ if (event->data_size) {
+ jData = env->NewByteArray(event->data_size);
+ jbyte *nData = env->GetByteArrayElements(jData, NULL);
+ memcpy(nData, (char *)event + event->data_offset, event->data_size);
+ env->ReleaseByteArrayElements(jData, nData, 0);
+ }
+
+ if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) {
+ struct sound_trigger_phrase_recognition_event *phraseEvent =
+ (struct sound_trigger_phrase_recognition_event *)event;
+
+ jobjectArray jExtras = env->NewObjectArray(phraseEvent->num_phrases,
+ gKeyPhraseRecognitionExtraClass, NULL);
+ if (jExtras == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < phraseEvent->num_phrases; i++) {
+ jintArray jConfidenceLevels = env->NewIntArray(phraseEvent->phrase_extras[i].num_users);
+ if (jConfidenceLevels == NULL) {
+ return;
+ }
+ jint *nConfidenceLevels = env->GetIntArrayElements(jConfidenceLevels, NULL);
+ memcpy(nConfidenceLevels,
+ phraseEvent->phrase_extras[i].confidence_levels,
+ phraseEvent->phrase_extras[i].num_users * sizeof(int));
+ env->ReleaseIntArrayElements(jConfidenceLevels, nConfidenceLevels, 0);
+ jobject jNewExtra = env->NewObject(gKeyPhraseRecognitionExtraClass,
+ gKeyPhraseRecognitionExtraCstor,
+ jConfidenceLevels,
+ phraseEvent->phrase_extras[i].recognition_modes);
+
+ if (jNewExtra == NULL) {
+ return;
+ }
+ env->SetObjectArrayElement(jExtras, i, jNewExtra);
+
+ }
+ jEvent = env->NewObject(gKeyPhraseRecognitionEventClass, gKeyPhraseRecognitionEventCstor,
+ event->status, event->model, event->capture_available,
+ event->capture_session, event->capture_delay_ms, jData,
+ phraseEvent->key_phrase_in_capture, jExtras);
+ } else {
+ jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor,
+ event->status, event->model, event->capture_available,
+ event->capture_session, event->capture_delay_ms, jData);
+ }
+
+
+ env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+ SOUNDTRIGGER_EVENT_RECOGNITION, 0, 0, jEvent);
+ if (env->ExceptionCheck()) {
+ ALOGW("An exception occurred while notifying an event.");
+ env->ExceptionClear();
+ }
+}
+
+void JNISoundTriggerCallback::onServiceDied()
+{
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+ SOUNDTRIGGER_EVENT_SERVICE_DIED, 0, 0, NULL);
+ if (env->ExceptionCheck()) {
+ ALOGW("An exception occurred while notifying an event.");
+ env->ExceptionClear();
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+static sp<SoundTrigger> getSoundTrigger(JNIEnv* env, jobject thiz)
+{
+ Mutex::Autolock l(gLock);
+ SoundTrigger* const st = (SoundTrigger*)env->GetLongField(thiz,
+ gModuleFields.mNativeContext);
+ return sp<SoundTrigger>(st);
+}
+
+static sp<SoundTrigger> setSoundTrigger(JNIEnv* env, jobject thiz, const sp<SoundTrigger>& module)
+{
+ Mutex::Autolock l(gLock);
+ sp<SoundTrigger> old = (SoundTrigger*)env->GetLongField(thiz,
+ gModuleFields.mNativeContext);
+ if (module.get()) {
+ module->incStrong((void*)setSoundTrigger);
+ }
+ if (old != 0) {
+ old->decStrong((void*)setSoundTrigger);
+ }
+ env->SetLongField(thiz, gModuleFields.mNativeContext, (jlong)module.get());
+ return old;
+}
+
+
+static jint
+android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz,
+ jobject jModules)
+{
+ ALOGV("listModules");
+
+ if (jModules == NULL) {
+ ALOGE("listModules NULL AudioPatch ArrayList");
+ return SOUNDTRIGGER_STATUS_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jModules, gArrayListClass)) {
+ ALOGE("listModules not an arraylist");
+ return SOUNDTRIGGER_STATUS_BAD_VALUE;
+ }
+
+ unsigned int numModules = 0;
+ struct sound_trigger_module_descriptor *nModules = NULL;
+
+ status_t status = SoundTrigger::listModules(nModules, &numModules);
+ if (status != NO_ERROR || numModules == 0) {
+ return (jint)status;
+ }
+
+ nModules = (struct sound_trigger_module_descriptor *)
+ calloc(numModules, sizeof(struct sound_trigger_module_descriptor));
+
+ status = SoundTrigger::listModules(nModules, &numModules);
+ ALOGV("listModules SoundTrigger::listModules status %d numModules %d", status, numModules);
+
+ if (status != NO_ERROR) {
+ numModules = 0;
+ }
+
+ for (size_t i = 0; i < numModules; i++) {
+ char str[SOUND_TRIGGER_MAX_STRING_LEN];
+
+ jstring implementor = env->NewStringUTF(nModules[i].properties.implementor);
+ jstring description = env->NewStringUTF(nModules[i].properties.description);
+ SoundTrigger::guidToString(&nModules[i].properties.uuid,
+ str,
+ SOUND_TRIGGER_MAX_STRING_LEN);
+ jstring uuid = env->NewStringUTF(str);
+
+ ALOGV("listModules module %d id %d description %s maxSoundModels %d",
+ i, nModules[i].handle, nModules[i].properties.description,
+ nModules[i].properties.max_sound_models);
+
+ jobject newModuleDesc = env->NewObject(gModulePropertiesClass, gModulePropertiesCstor,
+ nModules[i].handle,
+ implementor, description, uuid,
+ nModules[i].properties.version,
+ nModules[i].properties.max_sound_models,
+ nModules[i].properties.max_key_phrases,
+ nModules[i].properties.max_users,
+ nModules[i].properties.recognition_modes,
+ nModules[i].properties.capture_transition,
+ nModules[i].properties.max_buffer_ms,
+ nModules[i].properties.concurrent_capture,
+ nModules[i].properties.power_consumption_mw);
+
+ env->DeleteLocalRef(implementor);
+ env->DeleteLocalRef(description);
+ env->DeleteLocalRef(uuid);
+ if (newModuleDesc == NULL) {
+ status = SOUNDTRIGGER_STATUS_ERROR;
+ goto exit;
+ }
+ env->CallBooleanMethod(jModules, gArrayListMethods.add, newModuleDesc);
+ }
+
+exit:
+ free(nModules);
+ return (jint) status;
+}
+
+static void
+android_hardware_SoundTrigger_setup(JNIEnv *env, jobject thiz, jobject weak_this)
+{
+ ALOGV("setup");
+
+ sp<JNISoundTriggerCallback> callback = new JNISoundTriggerCallback(env, thiz, weak_this);
+
+ sound_trigger_module_handle_t handle =
+ (sound_trigger_module_handle_t)env->GetIntField(thiz, gModuleFields.mId);
+
+ sp<SoundTrigger> module = SoundTrigger::attach(handle, callback);
+ if (module == 0) {
+ return;
+ }
+
+ setSoundTrigger(env, thiz, module);
+}
+
+static void
+android_hardware_SoundTrigger_detach(JNIEnv *env, jobject thiz)
+{
+ ALOGV("detach");
+ sp<SoundTrigger> module = setSoundTrigger(env, thiz, 0);
+ ALOGV("detach module %p", module.get());
+ if (module != 0) {
+ ALOGV("detach module->detach()");
+ module->detach();
+ }
+}
+
+static void
+android_hardware_SoundTrigger_finalize(JNIEnv *env, jobject thiz)
+{
+ ALOGV("finalize");
+ sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+ if (module != 0) {
+ ALOGW("SoundTrigger finalized without being detached");
+ }
+ android_hardware_SoundTrigger_detach(env, thiz);
+}
+
+static jint
+android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz,
+ jobject jSoundModel, jintArray jHandle)
+{
+ jint status = SOUNDTRIGGER_STATUS_OK;
+ char *nData = NULL;
+ struct sound_trigger_sound_model *nSoundModel;
+ jbyteArray jData;
+ sp<MemoryDealer> memoryDealer;
+ sp<IMemory> memory;
+ size_t size;
+ sound_model_handle_t handle;
+
+ ALOGV("loadSoundModel");
+ sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+ if (module == NULL) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ if (jHandle == NULL) {
+ return SOUNDTRIGGER_STATUS_BAD_VALUE;
+ }
+ jsize jHandleLen = env->GetArrayLength(jHandle);
+ if (jHandleLen == 0) {
+ return SOUNDTRIGGER_STATUS_BAD_VALUE;
+ }
+ jint *nHandle = env->GetIntArrayElements(jHandle, NULL);
+ if (nHandle == NULL) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ if (!env->IsInstanceOf(jSoundModel, gSoundModelClass)) {
+ status = SOUNDTRIGGER_STATUS_BAD_VALUE;
+ goto exit;
+ }
+ size_t offset;
+ sound_trigger_sound_model_type_t type;
+ if (env->IsInstanceOf(jSoundModel, gKeyPhraseSoundModelClass)) {
+ offset = sizeof(struct sound_trigger_phrase_sound_model);
+ type = SOUND_MODEL_TYPE_KEYPHRASE;
+ } else {
+ offset = sizeof(struct sound_trigger_sound_model);
+ type = SOUND_MODEL_TYPE_UNKNOWN;
+ }
+ jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data);
+ if (jData == NULL) {
+ status = SOUNDTRIGGER_STATUS_BAD_VALUE;
+ goto exit;
+ }
+ size = env->GetArrayLength(jData);
+
+ nData = (char *)env->GetByteArrayElements(jData, NULL);
+ if (jData == NULL) {
+ status = SOUNDTRIGGER_STATUS_ERROR;
+ goto exit;
+ }
+
+ memoryDealer = new MemoryDealer(offset + size, "SoundTrigge-JNI::LoadModel");
+ if (memoryDealer == 0) {
+ status = SOUNDTRIGGER_STATUS_ERROR;
+ goto exit;
+ }
+ memory = memoryDealer->allocate(offset + size);
+ if (memory == 0 || memory->pointer() == NULL) {
+ status = SOUNDTRIGGER_STATUS_ERROR;
+ goto exit;
+ }
+
+ nSoundModel = (struct sound_trigger_sound_model *)memory->pointer();
+
+ nSoundModel->type = type;
+ nSoundModel->data_size = size;
+ nSoundModel->data_offset = offset;
+ memcpy((char *)nSoundModel + offset, nData, size);
+ if (type == SOUND_MODEL_TYPE_KEYPHRASE) {
+ struct sound_trigger_phrase_sound_model *phraseModel =
+ (struct sound_trigger_phrase_sound_model *)nSoundModel;
+
+ jobjectArray jPhrases =
+ (jobjectArray)env->GetObjectField(jSoundModel, gKeyPhraseSoundModelFields.keyPhrases);
+ if (jPhrases == NULL) {
+ status = SOUNDTRIGGER_STATUS_BAD_VALUE;
+ goto exit;
+ }
+
+ size_t numPhrases = env->GetArrayLength(jPhrases);
+ phraseModel->num_phrases = numPhrases;
+ ALOGV("loadSoundModel numPhrases %d", numPhrases);
+ for (size_t i = 0; i < numPhrases; i++) {
+ jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
+ phraseModel->phrases[i].recognition_mode =
+ env->GetIntField(jPhrase,gKeyPhraseFields.recognitionModes);
+ phraseModel->phrases[i].num_users =
+ env->GetIntField(jPhrase, gKeyPhraseFields.numUsers);
+ jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.locale);
+ const char *nLocale = env->GetStringUTFChars(jLocale, NULL);
+ strncpy(phraseModel->phrases[i].locale,
+ nLocale,
+ SOUND_TRIGGER_MAX_LOCALE_LEN);
+ jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.text);
+ const char *nText = env->GetStringUTFChars(jText, NULL);
+ strncpy(phraseModel->phrases[i].text,
+ nText,
+ SOUND_TRIGGER_MAX_STRING_LEN);
+
+ env->ReleaseStringUTFChars(jLocale, nLocale);
+ env->DeleteLocalRef(jLocale);
+ env->ReleaseStringUTFChars(jText, nText);
+ env->DeleteLocalRef(jText);
+ ALOGV("loadSoundModel phrases %d text %s locale %s",
+ i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale);
+ }
+ env->DeleteLocalRef(jPhrases);
+ }
+ status = module->loadSoundModel(memory, &handle);
+ ALOGV("loadSoundModel status %d handle %d", status, handle);
+
+exit:
+ if (nHandle != NULL) {
+ nHandle[0] = (jint)handle;
+ env->ReleaseIntArrayElements(jHandle, nHandle, NULL);
+ }
+ if (nData != NULL) {
+ env->ReleaseByteArrayElements(jData, (jbyte *)nData, NULL);
+ }
+ return status;
+}
+
+static jint
+android_hardware_SoundTrigger_unloadSoundModel(JNIEnv *env, jobject thiz,
+ jint jHandle)
+{
+ jint status = SOUNDTRIGGER_STATUS_OK;
+ ALOGV("unloadSoundModel");
+ sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+ if (module == NULL) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ status = module->unloadSoundModel((sound_model_handle_t)jHandle);
+
+ return status;
+}
+
+static jint
+android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz,
+ jint jHandle, jbyteArray jData)
+{
+ jint status = SOUNDTRIGGER_STATUS_OK;
+ ALOGV("startRecognition");
+ sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+ if (module == NULL) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ jsize dataSize = 0;
+ char *nData = NULL;
+ sp<IMemory> memory;
+ if (jData != NULL) {
+ dataSize = env->GetArrayLength(jData);
+ if (dataSize == 0) {
+ return SOUNDTRIGGER_STATUS_BAD_VALUE;
+ }
+ nData = (char *)env->GetByteArrayElements(jData, NULL);
+ if (nData == NULL) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ sp<MemoryDealer> memoryDealer =
+ new MemoryDealer(dataSize, "SoundTrigge-JNI::StartRecognition");
+ if (memoryDealer == 0) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ memory = memoryDealer->allocate(dataSize);
+ if (memory == 0 || memory->pointer() == NULL) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ memcpy(memory->pointer(), nData, dataSize);
+ }
+
+ status = module->startRecognition(jHandle, memory);
+ return status;
+}
+
+static jint
+android_hardware_SoundTrigger_stopRecognition(JNIEnv *env, jobject thiz,
+ jint jHandle)
+{
+ jint status = SOUNDTRIGGER_STATUS_OK;
+ ALOGV("stopRecognition");
+ sp<SoundTrigger> module = getSoundTrigger(env, thiz);
+ if (module == NULL) {
+ return SOUNDTRIGGER_STATUS_ERROR;
+ }
+ status = module->stopRecognition(jHandle);
+ return status;
+}
+
+static JNINativeMethod gMethods[] = {
+ {"listModules",
+ "(Ljava/util/ArrayList;)I",
+ (void *)android_hardware_SoundTrigger_listModules},
+};
+
+
+static JNINativeMethod gModuleMethods[] = {
+ {"native_setup",
+ "(Ljava/lang/Object;)V",
+ (void *)android_hardware_SoundTrigger_setup},
+ {"native_finalize",
+ "()V",
+ (void *)android_hardware_SoundTrigger_finalize},
+ {"detach",
+ "()V",
+ (void *)android_hardware_SoundTrigger_detach},
+ {"loadSoundModel",
+ "(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;[I)I",
+ (void *)android_hardware_SoundTrigger_loadSoundModel},
+ {"unloadSoundModel",
+ "(I)I",
+ (void *)android_hardware_SoundTrigger_unloadSoundModel},
+ {"startRecognition",
+ "(I[B)I",
+ (void *)android_hardware_SoundTrigger_startRecognition},
+ {"stopRecognition",
+ "(I)I",
+ (void *)android_hardware_SoundTrigger_stopRecognition},
+};
+
+int register_android_hardware_SoundTrigger(JNIEnv *env)
+{
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
+ gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass);
+ gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
+ jclass lClass = env->FindClass(kSoundTriggerClassPathName);
+ gSoundTriggerClass = (jclass) env->NewGlobalRef(lClass);
+
+ jclass moduleClass = env->FindClass(kModuleClassPathName);
+ gModuleClass = (jclass) env->NewGlobalRef(moduleClass);
+ gPostEventFromNative = env->GetStaticMethodID(moduleClass, "postEventFromNative",
+ "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ gModuleFields.mNativeContext = env->GetFieldID(moduleClass, "mNativeContext", "J");
+ gModuleFields.mId = env->GetFieldID(moduleClass, "mId", "I");
+
+
+ jclass modulePropertiesClass = env->FindClass(kModulePropertiesClassPathName);
+ gModulePropertiesClass = (jclass) env->NewGlobalRef(modulePropertiesClass);
+ gModulePropertiesCstor = env->GetMethodID(modulePropertiesClass, "<init>",
+ "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZI)V");
+
+ jclass soundModelClass = env->FindClass(kSoundModelClassPathName);
+ gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass);
+ gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B");
+
+ jclass keyPhraseClass = env->FindClass(kKeyPhraseClassPathName);
+ gKeyPhraseClass = (jclass) env->NewGlobalRef(keyPhraseClass);
+ gKeyPhraseFields.recognitionModes = env->GetFieldID(keyPhraseClass, "recognitionModes", "I");
+ gKeyPhraseFields.locale = env->GetFieldID(keyPhraseClass, "locale", "Ljava/lang/String;");
+ gKeyPhraseFields.text = env->GetFieldID(keyPhraseClass, "text", "Ljava/lang/String;");
+ gKeyPhraseFields.numUsers = env->GetFieldID(keyPhraseClass, "numUsers", "I");
+
+ jclass keyPhraseSoundModelClass = env->FindClass(kKeyPhraseSoundModelClassPathName);
+ gKeyPhraseSoundModelClass = (jclass) env->NewGlobalRef(keyPhraseSoundModelClass);
+ gKeyPhraseSoundModelFields.keyPhrases = env->GetFieldID(keyPhraseSoundModelClass,
+ "keyPhrases",
+ "[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhrase;");
+
+
+ jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName);
+ gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass);
+ gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "<init>",
+ "(IIZII[B)V");
+
+ jclass keyPhraseRecognitionEventClass = env->FindClass(kKeyPhraseRecognitionEventClassPathName);
+ gKeyPhraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionEventClass);
+ gKeyPhraseRecognitionEventCstor = env->GetMethodID(keyPhraseRecognitionEventClass, "<init>",
+ "(IIZII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra;)V");
+
+
+ jclass keyPhraseRecognitionExtraClass = env->FindClass(kKeyPhraseRecognitionExtraClassPathName);
+ gKeyPhraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionExtraClass);
+ gKeyPhraseRecognitionExtraCstor = env->GetMethodID(keyPhraseRecognitionExtraClass, "<init>",
+ "([II)V");
+
+ int status = AndroidRuntime::registerNativeMethods(env,
+ kSoundTriggerClassPathName, gMethods, NELEM(gMethods));
+
+ if (status == 0) {
+ status = AndroidRuntime::registerNativeMethods(env,
+ kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
+ }
+
+ return status;
+}
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index 7b686e7..33100bf 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -31,6 +31,8 @@
#include <utils/RefBase.h>
#include <cutils/properties.h>
+#include <string.h>
+
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_hardware_camera2_CameraMetadata.h"
@@ -175,7 +177,7 @@
}
static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPtr,
- jobject resultsPtr) {
+ jobject resultsPtr, jstring formattedCaptureTime) {
ALOGV("%s:", __FUNCTION__);
CameraMetadata characteristics;
CameraMetadata results;
@@ -205,7 +207,6 @@
OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
// TODO: Greensplit.
- // TODO: UniqueCameraModel
// TODO: Add remaining non-essential tags
{
// Set orientation
@@ -352,6 +353,176 @@
}
{
+ // image description
+ uint8_t imageDescription = '\0'; // empty
+ BAIL_IF_INVALID(writer->addEntry(TAG_IMAGEDESCRIPTION, 1, &imageDescription, TIFF_IFD_0),
+ env, TAG_IMAGEDESCRIPTION);
+ }
+
+ {
+ // make
+ char manufacturer[PROPERTY_VALUE_MAX];
+
+ // Use "" to represent unknown make as suggested in TIFF/EP spec.
+ property_get("ro.product.manufacturer", manufacturer, "");
+ uint32_t count = static_cast<uint32_t>(strlen(manufacturer)) + 1;
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_MAKE, count, reinterpret_cast<uint8_t*>(manufacturer),
+ TIFF_IFD_0), env, TAG_MAKE);
+ }
+
+ {
+ // model
+ char model[PROPERTY_VALUE_MAX];
+
+ // Use "" to represent unknown model as suggested in TIFF/EP spec.
+ property_get("ro.product.model", model, "");
+ uint32_t count = static_cast<uint32_t>(strlen(model)) + 1;
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_MODEL, count, reinterpret_cast<uint8_t*>(model),
+ TIFF_IFD_0), env, TAG_MODEL);
+ }
+
+ {
+ // x resolution
+ uint32_t xres[] = { 72, 1 }; // default 72 ppi
+ BAIL_IF_INVALID(writer->addEntry(TAG_XRESOLUTION, 1, xres, TIFF_IFD_0),
+ env, TAG_XRESOLUTION);
+
+ // y resolution
+ uint32_t yres[] = { 72, 1 }; // default 72 ppi
+ BAIL_IF_INVALID(writer->addEntry(TAG_YRESOLUTION, 1, yres, TIFF_IFD_0),
+ env, TAG_YRESOLUTION);
+
+ uint16_t unit = 2; // inches
+ BAIL_IF_INVALID(writer->addEntry(TAG_RESOLUTIONUNIT, 1, &unit, TIFF_IFD_0),
+ env, TAG_RESOLUTIONUNIT);
+ }
+
+ {
+ // software
+ char software[PROPERTY_VALUE_MAX];
+ property_get("ro.build.fingerprint", software, "");
+ uint32_t count = static_cast<uint32_t>(strlen(software)) + 1;
+ BAIL_IF_INVALID(writer->addEntry(TAG_SOFTWARE, count, reinterpret_cast<uint8_t*>(software),
+ TIFF_IFD_0), env, TAG_SOFTWARE);
+ }
+
+ {
+ // datetime
+ const size_t DATETIME_COUNT = 20;
+ const char* captureTime = env->GetStringUTFChars(formattedCaptureTime, NULL);
+
+ size_t len = strlen(captureTime) + 1;
+ if (len != DATETIME_COUNT) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Timestamp string length is not required 20 characters");
+ return;
+ }
+
+ BAIL_IF_INVALID(writer->addEntry(TAG_DATETIME, DATETIME_COUNT,
+ reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0), env, TAG_DATETIMEORIGINAL);
+
+ // datetime original
+ BAIL_IF_INVALID(writer->addEntry(TAG_DATETIMEORIGINAL, DATETIME_COUNT,
+ reinterpret_cast<const uint8_t*>(captureTime), TIFF_IFD_0), env, TAG_DATETIMEORIGINAL);
+ env->ReleaseStringUTFChars(formattedCaptureTime, captureTime);
+ }
+
+ {
+ // TIFF/EP standard id
+ uint8_t standardId[] = { 1, 0, 0, 0 };
+ BAIL_IF_INVALID(writer->addEntry(TAG_TIFFEPSTANDARDID, 4, standardId,
+ TIFF_IFD_0), env, TAG_TIFFEPSTANDARDID);
+ }
+
+ {
+ // copyright
+ uint8_t copyright = '\0'; // empty
+ BAIL_IF_INVALID(writer->addEntry(TAG_COPYRIGHT, 1, ©right,
+ TIFF_IFD_0), env, TAG_COPYRIGHT);
+ }
+
+ {
+ // exposure time
+ camera_metadata_entry entry =
+ results.find(ANDROID_SENSOR_EXPOSURE_TIME);
+ BAIL_IF_EMPTY(entry, env, TAG_EXPOSURETIME);
+
+ int64_t exposureTime = *(entry.data.i64);
+
+ if (exposureTime < 0) {
+ // Should be unreachable
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Negative exposure time in metadata");
+ return;
+ }
+
+ // Ensure exposure time doesn't overflow (for exposures > 4s)
+ uint32_t denominator = 1000000000;
+ while (exposureTime > UINT32_MAX) {
+ exposureTime >>= 1;
+ denominator >>= 1;
+ if (denominator == 0) {
+ // Should be unreachable
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Exposure time too long");
+ return;
+ }
+ }
+
+ uint32_t exposure[] = { static_cast<uint32_t>(exposureTime), denominator };
+ BAIL_IF_INVALID(writer->addEntry(TAG_EXPOSURETIME, 1, exposure,
+ TIFF_IFD_0), env, TAG_EXPOSURETIME);
+
+ }
+
+ {
+ // ISO speed ratings
+ camera_metadata_entry entry =
+ results.find(ANDROID_SENSOR_SENSITIVITY);
+ BAIL_IF_EMPTY(entry, env, TAG_ISOSPEEDRATINGS);
+
+ int32_t tempIso = *(entry.data.i32);
+ if (tempIso < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Negative ISO value");
+ return;
+ }
+
+ if (tempIso > UINT16_MAX) {
+ ALOGW("%s: ISO value overflows UINT16_MAX, clamping to max", __FUNCTION__);
+ tempIso = UINT16_MAX;
+ }
+
+ uint16_t iso = static_cast<uint16_t>(tempIso);
+ BAIL_IF_INVALID(writer->addEntry(TAG_ISOSPEEDRATINGS, 1, &iso,
+ TIFF_IFD_0), env, TAG_ISOSPEEDRATINGS);
+ }
+
+ {
+ // focal length
+ camera_metadata_entry entry =
+ results.find(ANDROID_LENS_FOCAL_LENGTH);
+ BAIL_IF_EMPTY(entry, env, TAG_FOCALLENGTH);
+
+ uint32_t focalLength[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
+ BAIL_IF_INVALID(writer->addEntry(TAG_FOCALLENGTH, 1, focalLength,
+ TIFF_IFD_0), env, TAG_FOCALLENGTH);
+ }
+
+ {
+ // f number
+ camera_metadata_entry entry =
+ results.find(ANDROID_LENS_APERTURE);
+ BAIL_IF_EMPTY(entry, env, TAG_FNUMBER);
+
+ uint32_t fnum[] = { static_cast<uint32_t>(*(entry.data.f) * 100), 100 };
+ BAIL_IF_INVALID(writer->addEntry(TAG_FNUMBER, 1, fnum,
+ TIFF_IFD_0), env, TAG_FNUMBER);
+ }
+
+ {
// Set DNG version information
uint8_t version[4] = {1, 4, 0, 0};
BAIL_IF_INVALID(writer->addEntry(TAG_DNGVERSION, 4, version, TIFF_IFD_0),
@@ -751,7 +922,8 @@
static JNINativeMethod gDngCreatorMethods[] = {
{"nativeClassInit", "()V", (void*) DngCreator_nativeClassInit},
{"nativeInit", "(Landroid/hardware/camera2/impl/CameraMetadataNative;"
- "Landroid/hardware/camera2/impl/CameraMetadataNative;)V", (void*) DngCreator_init},
+ "Landroid/hardware/camera2/impl/CameraMetadataNative;Ljava/lang/String;)V",
+ (void*) DngCreator_init},
{"nativeDestroy", "()V", (void*) DngCreator_destroy},
{"nativeSetOrientation", "(I)V", (void*) DngCreator_nativeSetOrientation},
{"nativeSetThumbnailBitmap","(Landroid/graphics/Bitmap;)V",
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 0f7e140..ee4c619 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -244,6 +244,12 @@
}
static jint
+android_media_AudioSystem_newAudioSessionId(JNIEnv *env, jobject thiz)
+{
+ return AudioSystem::newAudioSessionId();
+}
+
+static jint
android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs)
{
const jchar* c_keyValuePairs = env->GetStringCritical(keyValuePairs, 0);
@@ -613,8 +619,12 @@
goto exit;
}
}
- //TODO: replace popcount by audio utils function mask to count
- int numValues = popcount(nAudioPortConfig->gain.channel_mask);
+ int numValues;
+ if (useInMask) {
+ numValues = audio_channel_count_from_in_mask(nAudioPortConfig->gain.channel_mask);
+ } else {
+ numValues = audio_channel_count_from_out_mask(nAudioPortConfig->gain.channel_mask);
+ }
jGainValues = env->NewIntArray(numValues);
if (jGainValues == NULL) {
ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues);
@@ -1291,6 +1301,7 @@
{"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive},
{"isStreamActiveRemotely","(II)Z", (void *)android_media_AudioSystem_isStreamActiveRemotely},
{"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive},
+ {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId},
{"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
{"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState},
{"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 9141d44..9fa5ec9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -91,53 +91,9 @@
} gRectClassInfo;
// ----------------------------------------------------------------------------
-// Caching
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20Canvas_flushCaches(JNIEnv* env, jobject clazz,
- jint mode) {
- if (Caches::hasInstance()) {
- Caches::getInstance().flush(static_cast<Caches::FlushMode>(mode));
- }
-}
-
-static jboolean android_view_GLES20Canvas_initCaches(JNIEnv* env, jobject clazz) {
- if (Caches::hasInstance()) {
- return Caches::getInstance().init() ? JNI_TRUE : JNI_FALSE;
- }
- return JNI_FALSE;
-}
-
-static void android_view_GLES20Canvas_terminateCaches(JNIEnv* env, jobject clazz) {
- if (Caches::hasInstance()) {
- Caches::getInstance().terminate();
- }
-}
-
-// ----------------------------------------------------------------------------
-// Caching
-// ----------------------------------------------------------------------------
-
-static void android_view_GLES20Canvas_initAtlas(JNIEnv* env, jobject clazz,
- jobject graphicBuffer, jlongArray atlasMapArray, jint count) {
-
- sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
- jlong* jAtlasMap = env->GetLongArrayElements(atlasMapArray, NULL);
- Caches::getInstance().assetAtlas.init(buffer, jAtlasMap, count);
- env->ReleaseLongArrayElements(atlasMapArray, jAtlasMap, 0);
-}
-
-// ----------------------------------------------------------------------------
// Constructors
// ----------------------------------------------------------------------------
-static jlong android_view_GLES20Canvas_createRenderer(JNIEnv* env, jobject clazz) {
- RENDERER_LOGD("Create OpenGLRenderer");
- OpenGLRenderer* renderer = new OpenGLRenderer();
- renderer->initProperties();
- return reinterpret_cast<jlong>(renderer);
-}
-
static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject clazz,
jlong rendererPtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
@@ -174,10 +130,6 @@
renderer->finish();
}
-static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) {
- return Stencil::getStencilSize();
-}
-
static void android_view_GLES20Canvas_setProperty(JNIEnv* env,
jobject clazz, jstring name, jstring value) {
if (!Caches::hasInstance()) {
@@ -577,15 +529,6 @@
}
}
-static void android_view_GLES20Canvas_drawRects(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jfloatArray rects, jint count, jlong paintPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- jfloat* storage = env->GetFloatArrayElements(rects, NULL);
- SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
- renderer->drawRects(storage, count, paint);
- env->ReleaseFloatArrayElements(rects, storage, 0);
-}
-
static void android_view_GLES20Canvas_drawPoints(JNIEnv* env, jobject clazz,
jlong rendererPtr, jfloatArray points, jint offset, jint count, jlong paintPtr) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
@@ -632,6 +575,7 @@
// Text
// ----------------------------------------------------------------------------
+// TODO: this is moving to MinikinUtils, remove with USE_MINIKIN ifdef
static float xOffsetForTextAlign(SkPaint* paint, float totalAdvance) {
switch (paint->getTextAlign()) {
case SkPaint::kCenter_Align:
@@ -647,43 +591,50 @@
}
#ifdef USE_MINIKIN
+
+class RenderTextFunctor {
+public:
+ RenderTextFunctor(const Layout& layout, OpenGLRenderer* renderer, jfloat x, jfloat y,
+ SkPaint* paint, uint16_t* glyphs, float* pos, float totalAdvance,
+ uirenderer::Rect& bounds)
+ : layout(layout), renderer(renderer), x(x), y(y), paint(paint), glyphs(glyphs),
+ pos(pos), totalAdvance(totalAdvance), bounds(bounds) { }
+ void operator()(size_t start, size_t end) {
+ for (size_t i = start; i < end; i++) {
+ glyphs[i] = layout.getGlyphId(i);
+ pos[2 * i] = layout.getX(i);
+ pos[2 * i + 1] = layout.getY(i);
+ }
+ size_t glyphsCount = end - start;
+ int bytesCount = glyphsCount * sizeof(jchar);
+ renderer->drawText((const char*) (glyphs + start), bytesCount, glyphsCount,
+ x, y, pos + 2 * start, paint, totalAdvance, bounds);
+ }
+private:
+ const Layout& layout;
+ OpenGLRenderer* renderer;
+ jfloat x;
+ jfloat y;
+ SkPaint* paint;
+ uint16_t* glyphs;
+ float* pos;
+ float totalAdvance;
+ uirenderer::Rect& bounds;
+};
+
static void renderTextLayout(OpenGLRenderer* renderer, Layout* layout,
jfloat x, jfloat y, SkPaint* paint) {
size_t nGlyphs = layout->nGlyphs();
float* pos = new float[nGlyphs * 2];
uint16_t* glyphs = new uint16_t[nGlyphs];
- SkTypeface* lastFace = 0;
- SkTypeface* skFace = 0;
- size_t start = 0;
MinikinRect b;
layout->getBounds(&b);
android::uirenderer::Rect bounds(b.mLeft, b.mTop, b.mRight, b.mBottom);
bounds.translate(x, y);
float totalAdvance = layout->getAdvance();
- for (size_t i = 0; i < nGlyphs; i++) {
- MinikinFontSkia* mfs = static_cast<MinikinFontSkia *>(layout->getFont(i));
- skFace = mfs->GetSkTypeface();
- glyphs[i] = layout->getGlyphId(i);
- pos[2 * i] = layout->getX(i);
- pos[2 * i + 1] = layout->getY(i);
- if (i > 0 && skFace != lastFace) {
- paint->setTypeface(lastFace);
- size_t glyphsCount = i - start;
- int bytesCount = glyphsCount * sizeof(jchar);
- renderer->drawText((const char*) (glyphs + start), bytesCount, glyphsCount,
- x, y, pos + 2 * start, paint, totalAdvance, bounds);
- start = i;
- }
- lastFace = skFace;
- }
- if (skFace != NULL) {
- paint->setTypeface(skFace);
- size_t glyphsCount = nGlyphs - start;
- int bytesCount = glyphsCount * sizeof(jchar);
- renderer->drawText((const char*) (glyphs + start), bytesCount, glyphsCount,
- x, y, pos + 2 * start, paint, totalAdvance, bounds);
- }
+ RenderTextFunctor f(*layout, renderer, x, y, paint, glyphs, pos, totalAdvance, bounds);
+ MinikinUtils::forFontRun(*layout, paint, f);
delete[] glyphs;
delete[] pos;
}
@@ -693,8 +644,8 @@
jfloat x, jfloat y, int flags, SkPaint* paint, TypefaceImpl* typeface) {
#ifdef USE_MINIKIN
Layout layout;
- MinikinUtils::SetLayoutProperties(&layout, paint, flags, typeface);
- layout.doLayout(text, count);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface);
+ layout.doLayout(text, 0, count, count, css);
x += xOffsetForTextAlign(paint, layout.getAdvance());
renderTextLayout(renderer, &layout, x, y, paint);
#else
@@ -737,8 +688,8 @@
int flags, SkPaint* paint, TypefaceImpl* typeface) {
#ifdef USE_MINIKIN
Layout layout;
- MinikinUtils::SetLayoutProperties(&layout, paint, flags, typeface);
- layout.doLayout(text + start, count);
+ std::string css = MinikinUtils::setLayoutProperties(&layout, paint, flags, typeface);
+ layout.doLayout(text, start, count, contextCount, css);
x += xOffsetForTextAlign(paint, layout.getAdvance());
renderTextLayout(renderer, &layout, x, y, paint);
#else
@@ -924,39 +875,6 @@
renderer->drawLayer(layer, x, y);
}
-static jboolean android_view_GLES20Canvas_copyLayer(JNIEnv* env, jobject clazz,
- jlong layerPtr, jlong bitmapPtr) {
- Layer* layer = reinterpret_cast<Layer*>(layerPtr);
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr);
- return LayerRenderer::copyLayer(layer, bitmap);
-}
-
-static void android_view_GLES20Canvas_pushLayerUpdate(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jlong layerPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- Layer* layer = reinterpret_cast<Layer*>(layerPtr);
- renderer->pushLayerUpdate(layer);
-}
-
-static void android_view_GLES20Canvas_cancelLayerUpdate(JNIEnv* env, jobject clazz,
- jlong rendererPtr, jlong layerPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- Layer* layer = reinterpret_cast<Layer*>(layerPtr);
- renderer->cancelLayerUpdate(layer);
-}
-
-static void android_view_GLES20Canvas_clearLayerUpdates(JNIEnv* env, jobject clazz,
- jlong rendererPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- renderer->clearLayerUpdates();
-}
-
-static void android_view_GLES20Canvas_flushLayerUpdates(JNIEnv* env, jobject clazz,
- jlong rendererPtr) {
- OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- renderer->flushLayerUpdates();
-}
-
#endif // USE_OPENGL_RENDERER
// ----------------------------------------------------------------------------
@@ -1001,14 +919,7 @@
{ "nIsAvailable", "()Z", (void*) android_view_GLES20Canvas_isAvailable },
#ifdef USE_OPENGL_RENDERER
- { "nFlushCaches", "(I)V", (void*) android_view_GLES20Canvas_flushCaches },
- { "nInitCaches", "()Z", (void*) android_view_GLES20Canvas_initCaches },
- { "nTerminateCaches", "()V", (void*) android_view_GLES20Canvas_terminateCaches },
- { "nInitAtlas", "(Landroid/view/GraphicBuffer;[JI)V",
- (void*) android_view_GLES20Canvas_initAtlas },
-
- { "nCreateRenderer", "()J", (void*) android_view_GLES20Canvas_createRenderer },
{ "nDestroyRenderer", "(J)V", (void*) android_view_GLES20Canvas_destroyRenderer },
{ "nSetViewport", "(JII)V", (void*) android_view_GLES20Canvas_setViewport },
{ "nPrepare", "(JZ)I", (void*) android_view_GLES20Canvas_prepare },
@@ -1017,9 +928,6 @@
{ "nSetProperty", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*) android_view_GLES20Canvas_setProperty },
-
- { "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize },
-
{ "nCallDrawGLFunction", "(JJ)I", (void*) android_view_GLES20Canvas_callDrawGLFunction },
{ "nSave", "(JI)I", (void*) android_view_GLES20Canvas_save },
@@ -1059,7 +967,6 @@
{ "nDrawColor", "(JII)V", (void*) android_view_GLES20Canvas_drawColor },
{ "nDrawRect", "(JFFFFJ)V", (void*) android_view_GLES20Canvas_drawRect },
{ "nDrawRects", "(JJJ)V", (void*) android_view_GLES20Canvas_drawRegionAsRects },
- { "nDrawRects", "(J[FIJ)V", (void*) android_view_GLES20Canvas_drawRects },
{ "nDrawRoundRect", "(JFFFFFFJ)V", (void*) android_view_GLES20Canvas_drawRoundRect },
{ "nDrawCircle", "(JFFFJ)V", (void*) android_view_GLES20Canvas_drawCircle },
{ "nDrawCircle", "(JJJJJ)V", (void*) android_view_GLES20Canvas_drawCircleProps },
@@ -1099,11 +1006,6 @@
{ "nCreateDisplayListRenderer", "()J", (void*) android_view_GLES20Canvas_createDisplayListRenderer },
{ "nDrawLayer", "(JJFF)V", (void*) android_view_GLES20Canvas_drawLayer },
- { "nCopyLayer", "(JJ)Z", (void*) android_view_GLES20Canvas_copyLayer },
- { "nClearLayerUpdates", "(J)V", (void*) android_view_GLES20Canvas_clearLayerUpdates },
- { "nFlushLayerUpdates", "(J)V", (void*) android_view_GLES20Canvas_flushLayerUpdates },
- { "nPushLayerUpdate", "(JJ)V", (void*) android_view_GLES20Canvas_pushLayerUpdate },
- { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_GLES20Canvas_cancelLayerUpdate },
{ "nGetMaximumTextureWidth", "()I", (void*) android_view_GLES20Canvas_getMaxTextureWidth },
{ "nGetMaximumTextureHeight", "()I", (void*) android_view_GLES20Canvas_getMaxTextureHeight },
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
deleted file mode 100644
index d0269a3..0000000
--- a/core/jni/android_view_GLRenderer.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "GLRenderer"
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/egl_cache.h>
-
-#include <utils/Timers.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include <Caches.h>
-#include <Extensions.h>
-#include <LayerRenderer.h>
-#include <RenderNode.h>
-
-#ifdef USE_OPENGL_RENDERER
- EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
-#endif
-
-namespace android {
-
-/**
- * Note: OpenGLRenderer JNI layer is generated and compiled only on supported
- * devices. This means all the logic must be compiled only when the
- * preprocessor variable USE_OPENGL_RENDERER is defined.
- */
-#ifdef USE_OPENGL_RENDERER
-
-// ----------------------------------------------------------------------------
-// Defines
-// ----------------------------------------------------------------------------
-
-// Debug
-#define DEBUG_RENDERER 0
-
-// Debug
-#if DEBUG_RENDERER
- #define RENDERER_LOGD(...) ALOGD(__VA_ARGS__)
-#else
- #define RENDERER_LOGD(...)
-#endif
-
-// ----------------------------------------------------------------------------
-// Surface and display management
-// ----------------------------------------------------------------------------
-
-static jboolean android_view_GLRenderer_preserveBackBuffer(JNIEnv* env, jobject clazz) {
- EGLDisplay display = eglGetCurrentDisplay();
- EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
-
- eglGetError();
- eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
-
- EGLint error = eglGetError();
- if (error != EGL_SUCCESS) {
- RENDERER_LOGD("Could not enable buffer preserved swap behavior (%x)", error);
- }
-
- return error == EGL_SUCCESS;
-}
-
-static jboolean android_view_GLRenderer_isBackBufferPreserved(JNIEnv* env, jobject clazz) {
- EGLDisplay display = eglGetCurrentDisplay();
- EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
- EGLint value;
-
- eglGetError();
- eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &value);
-
- EGLint error = eglGetError();
- if (error != EGL_SUCCESS) {
- RENDERER_LOGD("Could not query buffer preserved swap behavior (%x)", error);
- }
-
- return error == EGL_SUCCESS && value == EGL_BUFFER_PRESERVED;
-}
-
-// ----------------------------------------------------------------------------
-// Tracing and debugging
-// ----------------------------------------------------------------------------
-
-static bool android_view_GLRenderer_loadProperties(JNIEnv* env, jobject clazz) {
- if (uirenderer::Caches::hasInstance()) {
- return uirenderer::Caches::getInstance().initProperties();
- }
- return false;
-}
-
-static void android_view_GLRenderer_beginFrame(JNIEnv* env, jobject clazz,
- jintArray size) {
-
- EGLDisplay display = eglGetCurrentDisplay();
- EGLSurface surface = eglGetCurrentSurface(EGL_DRAW);
-
- if (size) {
- EGLint value;
- jint* storage = env->GetIntArrayElements(size, NULL);
-
- eglQuerySurface(display, surface, EGL_WIDTH, &value);
- storage[0] = value;
-
- eglQuerySurface(display, surface, EGL_HEIGHT, &value);
- storage[1] = value;
-
- env->ReleaseIntArrayElements(size, storage, 0);
- }
-
- eglBeginFrame(display, surface);
-}
-
-static jlong android_view_GLRenderer_getSystemTime(JNIEnv* env, jobject clazz) {
- if (uirenderer::Extensions::getInstance().hasNvSystemTime()) {
- return eglGetSystemTimeNV();
- }
- return systemTime(SYSTEM_TIME_MONOTONIC);
-}
-
-static void android_view_GLRenderer_destroyLayer(JNIEnv* env, jobject clazz,
- jlong layerPtr) {
- using namespace android::uirenderer;
- Layer* layer = reinterpret_cast<Layer*>(layerPtr);
- LayerRenderer::destroyLayer(layer);
-}
-
-static void android_view_GLRenderer_prepareTree(JNIEnv* env, jobject clazz,
- jlong renderNodePtr) {
- using namespace android::uirenderer;
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- TreeInfo ignoredInfo;
- renderNode->prepareTree(ignoredInfo);
-}
-
-static void android_view_GLRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
- jlong functorPtr, jboolean hasContext) {
- using namespace android::uirenderer;
- Functor* functor = reinterpret_cast<Functor*>(functorPtr);
- DrawGlInfo::Mode mode = hasContext ? DrawGlInfo::kModeProcess : DrawGlInfo::kModeProcessNoContext;
- (*functor)(mode, NULL);
-}
-
-#endif // USE_OPENGL_RENDERER
-
-// ----------------------------------------------------------------------------
-// Shaders
-// ----------------------------------------------------------------------------
-
-static void android_view_GLRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
- jstring diskCachePath) {
-
- const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
- egl_cache_t::get()->setCacheFilename(cacheArray);
- env->ReleaseStringUTFChars(diskCachePath, cacheArray);
-}
-
-// ----------------------------------------------------------------------------
-// JNI Glue
-// ----------------------------------------------------------------------------
-
-const char* const kClassPathName = "android/view/GLRenderer";
-
-static JNINativeMethod gMethods[] = {
-#ifdef USE_OPENGL_RENDERER
- { "isBackBufferPreserved", "()Z", (void*) android_view_GLRenderer_isBackBufferPreserved },
- { "preserveBackBuffer", "()Z", (void*) android_view_GLRenderer_preserveBackBuffer },
- { "loadProperties", "()Z", (void*) android_view_GLRenderer_loadProperties },
-
- { "beginFrame", "([I)V", (void*) android_view_GLRenderer_beginFrame },
-
- { "getSystemTime", "()J", (void*) android_view_GLRenderer_getSystemTime },
- { "nDestroyLayer", "(J)V", (void*) android_view_GLRenderer_destroyLayer },
- { "nPrepareTree", "(J)V", (void*) android_view_GLRenderer_prepareTree },
- { "nInvokeFunctor", "(JZ)V", (void*) android_view_GLRenderer_invokeFunctor },
-#endif
-
- { "setupShadersDiskCache", "(Ljava/lang/String;)V",
- (void*) android_view_GLRenderer_setupShadersDiskCache },
-};
-
-int register_android_view_GLRenderer(JNIEnv* env) {
- return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
-}
-
-};
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index 33a2705..ace17ec 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -43,21 +43,6 @@
#ifdef USE_OPENGL_RENDERER
-static jlong android_view_HardwareLayer_createTextureLayer(JNIEnv* env, jobject clazz) {
- Layer* layer = LayerRenderer::createTextureLayer();
- if (!layer) return 0;
-
- return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer) );
-}
-
-static jlong android_view_HardwareLayer_createRenderLayer(JNIEnv* env, jobject clazz,
- jint width, jint height) {
- Layer* layer = LayerRenderer::createRenderLayer(width, height);
- if (!layer) return 0;
-
- return reinterpret_cast<jlong>( new DeferredLayerUpdater(layer) );
-}
-
static void android_view_HardwareLayer_onTextureDestroyed(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
@@ -102,21 +87,6 @@
layer->updateTexImage();
}
-static void android_view_HardwareLayer_updateRenderLayer(JNIEnv* env, jobject clazz,
- jlong layerUpdaterPtr, jlong displayListPtr,
- jint left, jint top, jint right, jint bottom) {
- DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
- RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
- layer->setDisplayList(displayList, left, top, right, bottom);
-}
-
-static jboolean android_view_HardwareLayer_flushChanges(JNIEnv* env, jobject clazz,
- jlong layerUpdaterPtr) {
- DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
- TreeInfo ignoredInfo;
- return layer->apply(ignoredInfo);
-}
-
static jlong android_view_HardwareLayer_getLayer(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
@@ -140,8 +110,6 @@
static JNINativeMethod gMethods[] = {
#ifdef USE_OPENGL_RENDERER
- { "nCreateTextureLayer", "()J", (void*) android_view_HardwareLayer_createTextureLayer },
- { "nCreateRenderLayer", "(II)J", (void*) android_view_HardwareLayer_createRenderLayer },
{ "nOnTextureDestroyed", "(J)V", (void*) android_view_HardwareLayer_onTextureDestroyed },
{ "nPrepare", "(JIIZ)Z", (void*) android_view_HardwareLayer_prepare },
@@ -150,9 +118,6 @@
{ "nSetSurfaceTexture", "(JLandroid/graphics/SurfaceTexture;Z)V",
(void*) android_view_HardwareLayer_setSurfaceTexture },
{ "nUpdateSurfaceTexture", "(J)V", (void*) android_view_HardwareLayer_updateSurfaceTexture },
- { "nUpdateRenderLayer", "(JJIIII)V", (void*) android_view_HardwareLayer_updateRenderLayer },
-
- { "nFlushChanges", "(J)Z", (void*) android_view_HardwareLayer_flushChanges },
{ "nGetLayer", "(J)J", (void*) android_view_HardwareLayer_getLayer },
{ "nGetTexName", "(J)I", (void*) android_view_HardwareLayer_getTexName },
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 26022e0..3ffde2d 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -38,6 +38,11 @@
*/
#ifdef USE_OPENGL_RENDERER
+#define SET_AND_DIRTY(prop, val, dirtyFlag) \
+ (reinterpret_cast<RenderNode*>(renderNodePtr)->mutateStagingProperties().prop(val) \
+ ? (reinterpret_cast<RenderNode*>(renderNodePtr)->setPropertyFieldsDirty(dirtyFlag), true) \
+ : false)
+
// ----------------------------------------------------------------------------
// DisplayList view properties
// ----------------------------------------------------------------------------
@@ -82,235 +87,199 @@
// RenderProperties - setters
// ----------------------------------------------------------------------------
-static void android_view_RenderNode_setCaching(JNIEnv* env,
- jobject clazz, jlong renderNodePtr, jboolean caching) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setCaching(caching);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+static jboolean android_view_RenderNode_setLayerType(JNIEnv* env,
+ jobject clazz, jlong renderNodePtr, jint jlayerType) {
+ LayerType layerType = static_cast<LayerType>(jlayerType);
+ return SET_AND_DIRTY(mutateLayerProperties().setType, layerType, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setStaticMatrix(JNIEnv* env,
+static jboolean android_view_RenderNode_setLayerPaint(JNIEnv* env,
+ jobject clazz, jlong renderNodePtr, jlong paintPtr) {
+ SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+ return SET_AND_DIRTY(mutateLayerProperties().setFromPaint, paint, RenderNode::GENERIC);
+}
+
+static jboolean android_view_RenderNode_setStaticMatrix(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jlong matrixPtr) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
- renderNode->mutateStagingProperties().setStaticMatrix(matrix);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setStaticMatrix, matrix, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setAnimationMatrix(JNIEnv* env,
+static jboolean android_view_RenderNode_setAnimationMatrix(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jlong matrixPtr) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
- renderNode->mutateStagingProperties().setAnimationMatrix(matrix);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setAnimationMatrix, matrix, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setClipToBounds(JNIEnv* env,
+static jboolean android_view_RenderNode_setClipToBounds(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jboolean clipToBounds) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setClipToBounds(clipToBounds);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setClipToBounds, clipToBounds, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setProjectBackwards(JNIEnv* env,
+static jboolean android_view_RenderNode_setProjectBackwards(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jboolean shouldProject) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setProjectBackwards(shouldProject);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setProjectBackwards, shouldProject, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setProjectionReceiver(JNIEnv* env,
+static jboolean android_view_RenderNode_setProjectionReceiver(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jboolean shouldRecieve) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setProjectionReceiver(shouldRecieve);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setProjectionReceiver, shouldRecieve, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setOutlineRoundRect(JNIEnv* env,
+static jboolean android_view_RenderNode_setOutlineRoundRect(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jint left, jint top,
jint right, jint bottom, jfloat radius) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom, radius);
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
}
-static void android_view_RenderNode_setOutlineConvexPath(JNIEnv* env,
+static jboolean android_view_RenderNode_setOutlineConvexPath(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jlong outlinePathPtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr);
renderNode->mutateStagingProperties().mutableOutline().setConvexPath(outlinePath);
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
}
-static void android_view_RenderNode_setOutlineEmpty(JNIEnv* env,
+static jboolean android_view_RenderNode_setOutlineEmpty(JNIEnv* env,
jobject clazz, jlong renderNodePtr) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableOutline().setEmpty();
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
}
-static void android_view_RenderNode_setClipToOutline(JNIEnv* env,
+static jboolean android_view_RenderNode_setClipToOutline(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jboolean clipToOutline) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableOutline().setShouldClip(clipToOutline);
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
}
-static void android_view_RenderNode_setRevealClip(JNIEnv* env,
+static jboolean android_view_RenderNode_setRevealClip(JNIEnv* env,
jobject clazz, jlong renderNodePtr, jboolean shouldClip, jboolean inverseClip,
jfloat x, jfloat y, jfloat radius) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
renderNode->mutateStagingProperties().mutableRevealClip().set(
shouldClip, inverseClip, x, y, radius);
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return true;
}
-static void android_view_RenderNode_setAlpha(JNIEnv* env,
+static jboolean android_view_RenderNode_setAlpha(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float alpha) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setAlpha(alpha);
- renderNode->setPropertyFieldsDirty(RenderNode::ALPHA);
+ return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA);
}
-static void android_view_RenderNode_setHasOverlappingRendering(JNIEnv* env,
+static jboolean android_view_RenderNode_setHasOverlappingRendering(JNIEnv* env,
jobject clazz, jlong renderNodePtr, bool hasOverlappingRendering) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setHasOverlappingRendering(hasOverlappingRendering);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
+ RenderNode::GENERIC);
}
-static void android_view_RenderNode_setElevation(JNIEnv* env,
+static jboolean android_view_RenderNode_setElevation(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float elevation) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setElevation(elevation);
- renderNode->setPropertyFieldsDirty(RenderNode::Z);
+ return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z);
}
-static void android_view_RenderNode_setTranslationX(JNIEnv* env,
+static jboolean android_view_RenderNode_setTranslationX(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float tx) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setTranslationX(tx);
- renderNode->setPropertyFieldsDirty(RenderNode::TRANSLATION_X | RenderNode::X);
+ return SET_AND_DIRTY(setTranslationX, tx, RenderNode::TRANSLATION_X | RenderNode::X);
}
-static void android_view_RenderNode_setTranslationY(JNIEnv* env,
+static jboolean android_view_RenderNode_setTranslationY(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float ty) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setTranslationY(ty);
- renderNode->setPropertyFieldsDirty(RenderNode::TRANSLATION_Y | RenderNode::Y);
+ return SET_AND_DIRTY(setTranslationY, ty, RenderNode::TRANSLATION_Y | RenderNode::Y);
}
-static void android_view_RenderNode_setTranslationZ(JNIEnv* env,
+static jboolean android_view_RenderNode_setTranslationZ(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float tz) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setTranslationZ(tz);
- renderNode->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z | RenderNode::Z);
+ return SET_AND_DIRTY(setTranslationZ, tz, RenderNode::TRANSLATION_Z | RenderNode::Z);
}
-static void android_view_RenderNode_setRotation(JNIEnv* env,
+static jboolean android_view_RenderNode_setRotation(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float rotation) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setRotation(rotation);
- renderNode->setPropertyFieldsDirty(RenderNode::ROTATION);
+ return SET_AND_DIRTY(setRotation, rotation, RenderNode::ROTATION);
}
-static void android_view_RenderNode_setRotationX(JNIEnv* env,
+static jboolean android_view_RenderNode_setRotationX(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float rx) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setRotationX(rx);
- renderNode->setPropertyFieldsDirty(RenderNode::ROTATION_X);
+ return SET_AND_DIRTY(setRotationX, rx, RenderNode::ROTATION_X);
}
-static void android_view_RenderNode_setRotationY(JNIEnv* env,
+static jboolean android_view_RenderNode_setRotationY(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float ry) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setRotationY(ry);
- renderNode->setPropertyFieldsDirty(RenderNode::ROTATION_Y);
+ return SET_AND_DIRTY(setRotationY, ry, RenderNode::ROTATION_Y);
}
-static void android_view_RenderNode_setScaleX(JNIEnv* env,
+static jboolean android_view_RenderNode_setScaleX(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float sx) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setScaleX(sx);
- renderNode->setPropertyFieldsDirty(RenderNode::SCALE_X);
+ return SET_AND_DIRTY(setScaleX, sx, RenderNode::SCALE_X);
}
-static void android_view_RenderNode_setScaleY(JNIEnv* env,
+static jboolean android_view_RenderNode_setScaleY(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float sy) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setScaleY(sy);
- renderNode->setPropertyFieldsDirty(RenderNode::SCALE_Y);
+ return SET_AND_DIRTY(setScaleY, sy, RenderNode::SCALE_Y);
}
-static void android_view_RenderNode_setPivotX(JNIEnv* env,
+static jboolean android_view_RenderNode_setPivotX(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float px) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setPivotX(px);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setPivotX, px, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setPivotY(JNIEnv* env,
+static jboolean android_view_RenderNode_setPivotY(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float py) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setPivotY(py);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setPivotY, py, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setCameraDistance(JNIEnv* env,
+static jboolean android_view_RenderNode_setCameraDistance(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float distance) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setCameraDistance(distance);
- renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+ return SET_AND_DIRTY(setCameraDistance, distance, RenderNode::GENERIC);
}
-static void android_view_RenderNode_setLeft(JNIEnv* env,
+static jboolean android_view_RenderNode_setLeft(JNIEnv* env,
jobject clazz, jlong renderNodePtr, int left) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setLeft(left);
- renderNode->setPropertyFieldsDirty(RenderNode::X);
+ return SET_AND_DIRTY(setLeft, left, RenderNode::X);
}
-static void android_view_RenderNode_setTop(JNIEnv* env,
+static jboolean android_view_RenderNode_setTop(JNIEnv* env,
jobject clazz, jlong renderNodePtr, int top) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setTop(top);
- renderNode->setPropertyFieldsDirty(RenderNode::Y);
+ return SET_AND_DIRTY(setTop, top, RenderNode::Y);
}
-static void android_view_RenderNode_setRight(JNIEnv* env,
+static jboolean android_view_RenderNode_setRight(JNIEnv* env,
jobject clazz, jlong renderNodePtr, int right) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setRight(right);
- renderNode->setPropertyFieldsDirty(RenderNode::X);
+ return SET_AND_DIRTY(setRight, right, RenderNode::X);
}
-static void android_view_RenderNode_setBottom(JNIEnv* env,
+static jboolean android_view_RenderNode_setBottom(JNIEnv* env,
jobject clazz, jlong renderNodePtr, int bottom) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setBottom(bottom);
- renderNode->setPropertyFieldsDirty(RenderNode::Y);
+ return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y);
}
-static void android_view_RenderNode_setLeftTopRightBottom(JNIEnv* env,
+static jboolean android_view_RenderNode_setLeftTopRightBottom(JNIEnv* env,
jobject clazz, jlong renderNodePtr, int left, int top,
int right, int bottom) {
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
- renderNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ if (renderNode->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom)) {
+ renderNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ return true;
+ }
+ return false;
}
-static void android_view_RenderNode_offsetLeftAndRight(JNIEnv* env,
+static jboolean android_view_RenderNode_offsetLeftAndRight(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float offset) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().offsetLeftRight(offset);
- renderNode->setPropertyFieldsDirty(RenderNode::X);
+ return SET_AND_DIRTY(offsetLeftRight, offset, RenderNode::X);
}
-static void android_view_RenderNode_offsetTopAndBottom(JNIEnv* env,
+static jboolean android_view_RenderNode_offsetTopAndBottom(JNIEnv* env,
jobject clazz, jlong renderNodePtr, float offset) {
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
- renderNode->mutateStagingProperties().offsetTopBottom(offset);
- renderNode->setPropertyFieldsDirty(RenderNode::Y);
+ return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y);
}
// ----------------------------------------------------------------------------
@@ -513,41 +482,42 @@
{ "nOutput", "(J)V", (void*) android_view_RenderNode_output },
{ "nGetDebugSize", "(J)I", (void*) android_view_RenderNode_getDebugSize },
- { "nSetCaching", "(JZ)V", (void*) android_view_RenderNode_setCaching },
- { "nSetStaticMatrix", "(JJ)V", (void*) android_view_RenderNode_setStaticMatrix },
- { "nSetAnimationMatrix", "(JJ)V", (void*) android_view_RenderNode_setAnimationMatrix },
- { "nSetClipToBounds", "(JZ)V", (void*) android_view_RenderNode_setClipToBounds },
- { "nSetProjectBackwards", "(JZ)V", (void*) android_view_RenderNode_setProjectBackwards },
- { "nSetProjectionReceiver","(JZ)V", (void*) android_view_RenderNode_setProjectionReceiver },
+ { "nSetLayerType", "(JI)Z", (void*) android_view_RenderNode_setLayerType },
+ { "nSetLayerPaint", "(JJ)Z", (void*) android_view_RenderNode_setLayerPaint },
+ { "nSetStaticMatrix", "(JJ)Z", (void*) android_view_RenderNode_setStaticMatrix },
+ { "nSetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_setAnimationMatrix },
+ { "nSetClipToBounds", "(JZ)Z", (void*) android_view_RenderNode_setClipToBounds },
+ { "nSetProjectBackwards", "(JZ)Z", (void*) android_view_RenderNode_setProjectBackwards },
+ { "nSetProjectionReceiver","(JZ)Z", (void*) android_view_RenderNode_setProjectionReceiver },
- { "nSetOutlineRoundRect", "(JIIIIF)V", (void*) android_view_RenderNode_setOutlineRoundRect },
- { "nSetOutlineConvexPath", "(JJ)V", (void*) android_view_RenderNode_setOutlineConvexPath },
- { "nSetOutlineEmpty", "(J)V", (void*) android_view_RenderNode_setOutlineEmpty },
- { "nSetClipToOutline", "(JZ)V", (void*) android_view_RenderNode_setClipToOutline },
- { "nSetRevealClip", "(JZZFFF)V", (void*) android_view_RenderNode_setRevealClip },
+ { "nSetOutlineRoundRect", "(JIIIIF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
+ { "nSetOutlineConvexPath", "(JJ)Z", (void*) android_view_RenderNode_setOutlineConvexPath },
+ { "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
+ { "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline },
+ { "nSetRevealClip", "(JZZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
- { "nSetAlpha", "(JF)V", (void*) android_view_RenderNode_setAlpha },
- { "nSetHasOverlappingRendering", "(JZ)V",
+ { "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha },
+ { "nSetHasOverlappingRendering", "(JZ)Z",
(void*) android_view_RenderNode_setHasOverlappingRendering },
- { "nSetElevation", "(JF)V", (void*) android_view_RenderNode_setElevation },
- { "nSetTranslationX", "(JF)V", (void*) android_view_RenderNode_setTranslationX },
- { "nSetTranslationY", "(JF)V", (void*) android_view_RenderNode_setTranslationY },
- { "nSetTranslationZ", "(JF)V", (void*) android_view_RenderNode_setTranslationZ },
- { "nSetRotation", "(JF)V", (void*) android_view_RenderNode_setRotation },
- { "nSetRotationX", "(JF)V", (void*) android_view_RenderNode_setRotationX },
- { "nSetRotationY", "(JF)V", (void*) android_view_RenderNode_setRotationY },
- { "nSetScaleX", "(JF)V", (void*) android_view_RenderNode_setScaleX },
- { "nSetScaleY", "(JF)V", (void*) android_view_RenderNode_setScaleY },
- { "nSetPivotX", "(JF)V", (void*) android_view_RenderNode_setPivotX },
- { "nSetPivotY", "(JF)V", (void*) android_view_RenderNode_setPivotY },
- { "nSetCameraDistance", "(JF)V", (void*) android_view_RenderNode_setCameraDistance },
- { "nSetLeft", "(JI)V", (void*) android_view_RenderNode_setLeft },
- { "nSetTop", "(JI)V", (void*) android_view_RenderNode_setTop },
- { "nSetRight", "(JI)V", (void*) android_view_RenderNode_setRight },
- { "nSetBottom", "(JI)V", (void*) android_view_RenderNode_setBottom },
- { "nSetLeftTopRightBottom","(JIIII)V", (void*) android_view_RenderNode_setLeftTopRightBottom },
- { "nOffsetLeftAndRight", "(JF)V", (void*) android_view_RenderNode_offsetLeftAndRight },
- { "nOffsetTopAndBottom", "(JF)V", (void*) android_view_RenderNode_offsetTopAndBottom },
+ { "nSetElevation", "(JF)Z", (void*) android_view_RenderNode_setElevation },
+ { "nSetTranslationX", "(JF)Z", (void*) android_view_RenderNode_setTranslationX },
+ { "nSetTranslationY", "(JF)Z", (void*) android_view_RenderNode_setTranslationY },
+ { "nSetTranslationZ", "(JF)Z", (void*) android_view_RenderNode_setTranslationZ },
+ { "nSetRotation", "(JF)Z", (void*) android_view_RenderNode_setRotation },
+ { "nSetRotationX", "(JF)Z", (void*) android_view_RenderNode_setRotationX },
+ { "nSetRotationY", "(JF)Z", (void*) android_view_RenderNode_setRotationY },
+ { "nSetScaleX", "(JF)Z", (void*) android_view_RenderNode_setScaleX },
+ { "nSetScaleY", "(JF)Z", (void*) android_view_RenderNode_setScaleY },
+ { "nSetPivotX", "(JF)Z", (void*) android_view_RenderNode_setPivotX },
+ { "nSetPivotY", "(JF)Z", (void*) android_view_RenderNode_setPivotY },
+ { "nSetCameraDistance", "(JF)Z", (void*) android_view_RenderNode_setCameraDistance },
+ { "nSetLeft", "(JI)Z", (void*) android_view_RenderNode_setLeft },
+ { "nSetTop", "(JI)Z", (void*) android_view_RenderNode_setTop },
+ { "nSetRight", "(JI)Z", (void*) android_view_RenderNode_setRight },
+ { "nSetBottom", "(JI)Z", (void*) android_view_RenderNode_setBottom },
+ { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
+ { "nOffsetLeftAndRight", "(JF)Z", (void*) android_view_RenderNode_offsetLeftAndRight },
+ { "nOffsetTopAndBottom", "(JF)Z", (void*) android_view_RenderNode_offsetTopAndBottom },
{ "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering },
{ "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline },
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 11f87cc..3d14aaf 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -95,6 +95,7 @@
env->GetLongField(surfaceObj, gSurfaceClassInfo.mNativeObject));
env->MonitorExit(lock);
}
+ env->DeleteLocalRef(lock);
return sur;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 4594cc3..c0d5221 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -42,6 +42,8 @@
#include <ScopedUtfChars.h>
+#include "SkTemplates.h"
+
// ----------------------------------------------------------------------------
namespace android {
@@ -125,7 +127,7 @@
int bottom = env->GetIntField(sourceCropObj, gRectClassInfo.bottom);
Rect sourceCrop(left, top, right, bottom);
- ScreenshotClient* screenshot = new ScreenshotClient();
+ SkAutoTDelete<ScreenshotClient> screenshot(new ScreenshotClient());
status_t res;
if (width > 0 && height > 0) {
if (allLayers) {
@@ -139,7 +141,6 @@
res = screenshot->update(displayToken, sourceCrop, useIdentityTransform);
}
if (res != NO_ERROR) {
- delete screenshot;
return NULL;
}
@@ -164,7 +165,6 @@
break;
}
default: {
- delete screenshot;
return NULL;
}
}
@@ -178,7 +178,7 @@
// takes ownership of ScreenshotClient
SkMallocPixelRef* pixels = SkMallocPixelRef::NewWithProc(screenshotInfo,
(size_t) rowBytes, NULL, (void*) screenshot->getPixels(), &DeleteScreenshot,
- (void*) screenshot);
+ (void*) (screenshot.detach()));
pixels->setImmutable();
bitmap->setPixelRef(pixels)->unref();
bitmap->lockPixels();
@@ -412,20 +412,12 @@
return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
}
-static void nativeBlankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
+static void nativeSetDisplayPowerMode(JNIEnv* env, jclass clazz, jobject tokenObj, jint mode) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return;
- ALOGD_IF_SLOW(100, "Excessive delay in blankDisplay() while turning screen off");
- SurfaceComposerClient::blankDisplay(token);
-}
-
-static void nativeUnblankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return;
-
- ALOGD_IF_SLOW(100, "Excessive delay in unblankDisplay() while turning screen on");
- SurfaceComposerClient::unblankDisplay(token);
+ ALOGD_IF_SLOW(100, "Excessive delay in setPowerMode()");
+ SurfaceComposerClient::setDisplayPowerMode(token, mode);
}
static jboolean nativeClearContentFrameStats(JNIEnv* env, jclass clazz, jlong nativeObject) {
@@ -628,10 +620,6 @@
(void*)nativeGetActiveConfig },
{"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z",
(void*)nativeSetActiveConfig },
- {"nativeBlankDisplay", "(Landroid/os/IBinder;)V",
- (void*)nativeBlankDisplay },
- {"nativeUnblankDisplay", "(Landroid/os/IBinder;)V",
- (void*)nativeUnblankDisplay },
{"nativeClearContentFrameStats", "(J)Z",
(void*)nativeClearContentFrameStats },
{"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z",
@@ -640,6 +628,8 @@
(void*)nativeClearAnimationFrameStats },
{"nativeGetAnimationFrameStats", "(Landroid/view/WindowAnimationFrameStats;)Z",
(void*)nativeGetAnimationFrameStats },
+ {"nativeSetDisplayPowerMode", "(Landroid/os/IBinder;I)V",
+ (void*)nativeSetDisplayPowerMode },
};
int register_android_view_SurfaceControl(JNIEnv* env)
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 1397131..815c4a7 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -22,6 +22,10 @@
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <EGL/egl_cache.h>
+
#include <utils/StrongPointer.h>
#include <android_runtime/android_view_Surface.h>
#include <system/window.h>
@@ -146,6 +150,13 @@
}
}
+protected:
+ virtual void damageSelf(TreeInfo& info) {
+ // Intentionally a no-op. As RootRenderNode gets a new DisplayListData
+ // every frame this would result in every draw push being a full inval,
+ // which is wrong. Only RootRenderNode has this issue.
+ }
+
private:
sp<Looper> mLooper;
std::vector<OnFinishedEvent> mOnFinishedEvents;
@@ -238,11 +249,9 @@
}
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
- jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density,
- jint dirtyLeft, jint dirtyTop, jint dirtyRight, jint dirtyBottom) {
+ jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density,
- dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+ return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density);
}
static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
@@ -329,6 +338,18 @@
#endif
// ----------------------------------------------------------------------------
+// Shaders
+// ----------------------------------------------------------------------------
+
+static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
+ jstring diskCachePath) {
+
+ const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
+ egl_cache_t::get()->setCacheFilename(cacheArray);
+ env->ReleaseStringUTFChars(diskCachePath, cacheArray);
+}
+
+// ----------------------------------------------------------------------------
// JNI Glue
// ----------------------------------------------------------------------------
@@ -347,7 +368,7 @@
{ "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
{ "nSetup", "(JIIFFFF)V", (void*) android_view_ThreadedRenderer_setup },
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
- { "nSyncAndDrawFrame", "(JJJFIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+ { "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
{ "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
{ "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },
@@ -361,6 +382,8 @@
{ "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
{ "nDumpProfileInfo", "(JLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
#endif
+ { "setupShadersDiskCache", "(Ljava/lang/String;)V",
+ (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 230658f..e55e4ea 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -46,6 +46,9 @@
#define LIB_SUFFIX ".so"
#define LIB_SUFFIX_LEN (sizeof(LIB_SUFFIX) - 1)
+#define RS_BITCODE_SUFFIX ".bc"
+#define RS_BITCODE_SUFFIX_LEN (sizeof(RS_BITCODE_SUFFIX) -1)
+
#define GDBSERVER "gdbserver"
#define GDBSERVER_LEN (sizeof(GDBSERVER) - 1)
@@ -486,6 +489,42 @@
return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch);
}
+enum bitcode_scan_result_t {
+ APK_SCAN_ERROR = -1,
+ NO_BITCODE_PRESENT = 0,
+ BITCODE_PRESENT = 1,
+};
+
+static jint
+com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode(JNIEnv *env, jclass clazz,
+ jlong apkHandle) {
+ ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+ void* cookie = NULL;
+ if (!zipFile->startIteration(&cookie)) {
+ return APK_SCAN_ERROR;
+ }
+
+ char fileName[PATH_MAX];
+ ZipEntryRO next = NULL;
+ while ((next = zipFile->nextEntry(cookie)) != NULL) {
+ if (zipFile->getEntryFileName(next, fileName, sizeof(fileName))) {
+ continue;
+ }
+
+ const size_t fileNameLen = strlen(fileName);
+ const char* lastSlash = strrchr(fileName, '/');
+ const char* baseName = (lastSlash == NULL) ? fileName : fileName + 1;
+ if (!strncmp(fileName + fileNameLen - RS_BITCODE_SUFFIX_LEN, RS_BITCODE_SUFFIX,
+ RS_BITCODE_SUFFIX_LEN) && isFilenameSafe(baseName)) {
+ zipFile->endIteration(cookie);
+ return BITCODE_PRESENT;
+ }
+ }
+
+ zipFile->endIteration(cookie);
+ return NO_BITCODE_PRESENT;
+}
+
static jlong
com_android_internal_content_NativeLibraryHelper_openApk(JNIEnv *env, jclass, jstring apkPath)
{
@@ -517,6 +556,8 @@
{"nativeFindSupportedAbi",
"(J[Ljava/lang/String;)I",
(void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
+ {"hasRenderscriptBitcode", "(J)I",
+ (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
};
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bb6a1cb..3067cdd0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -74,6 +74,8 @@
<protected-broadcast android:name="android.intent.action.USER_FOREGROUND" />
<protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
+ <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
+
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
<protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
<protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
@@ -299,7 +301,7 @@
android:label="@string/permlab_sendSms"
android:description="@string/permdesc_sendSms" />
- <!-- Allows an application (Phone) to send a request to other applications
+ <!-- @SystemApi Allows an application (Phone) to send a request to other applications
to handle the respond-via-message action during incoming calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
@@ -324,7 +326,7 @@
android:label="@string/permlab_receiveMms"
android:description="@string/permdesc_receiveMms" />
- <!-- Allows an application to receive emergency cell broadcast messages,
+ <!-- @SystemApi Allows an application to receive emergency cell broadcast messages,
to record or display them to the user.
<p>Not for use by third-party applications.
@hide Pending API council approval -->
@@ -404,10 +406,9 @@
android:label="@string/permlab_writeContacts"
android:description="@string/permdesc_writeContacts" />
- <!-- Allows an application to execute contacts directory search.
+ <!-- @SystemApi @hide Allows an application to execute contacts directory search.
This should only be used by ContactsProvider.
<p>Not for use by third-party applications. -->
- <!-- @hide -->
<permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
android:permissionGroup="android.permission-group.PERSONAL_INFO"
android:protectionLevel="signature|system" />
@@ -692,20 +693,20 @@
android:label="@string/permlab_accessLocationExtraCommands"
android:description="@string/permdesc_accessLocationExtraCommands" />
- <!-- Allows an application to install a location provider into the Location Manager.
+ <!-- @SystemApi Allows an application to install a location provider into the Location Manager.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
android:protectionLevel="signature|system"
android:label="@string/permlab_installLocationProvider"
android:description="@string/permdesc_installLocationProvider" />
- <!-- Allows HDMI-CEC service to access device and configuration files.
- @hide This should only be used by HDMI-CEC service.
+ <!-- @SystemApi @hide Allows HDMI-CEC service to access device and configuration files.
+ This should only be used by HDMI-CEC service.
-->
<permission android:name="android.permission.HDMI_CEC"
android:protectionLevel="signatureOrSystem" />
- <!-- Allows an application to use location features in hardware,
+ <!-- @SystemApi Allows an application to use location features in hardware,
such as the geofencing api.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.LOCATION_HARDWARE"
@@ -803,7 +804,7 @@
android:description="@string/permdesc_bluetoothAdmin"
android:label="@string/permlab_bluetoothAdmin" />
- <!-- Allows applications to pair bluetooth devices without user interaction.
+ <!-- @SystemApi Allows applications to pair bluetooth devices without user interaction.
This is not available to third party applications. -->
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
android:permissionGroup="android.permission-group.BLUETOOTH_NETWORK"
@@ -825,18 +826,18 @@
android:description="@string/permdesc_nfc"
android:label="@string/permlab_nfc" />
- <!-- Allows an internal user to use privileged ConnectivityManager APIs.
+ <!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
@hide -->
<permission android:name="android.permission.CONNECTIVITY_INTERNAL"
android:permissionGroup="android.permission-group.NETWORK"
android:protectionLevel="signature|system" />
- <!-- @hide -->
+ <!-- @SystemApi @hide -->
<permission android:name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"
android:permissionGroup="android.permission-group.NETWORK"
android:protectionLevel="signature|system" />
- <!-- Allows access to the loop radio (Android@Home mesh network) device.
+ <!-- @SystemApi Allows access to the loop radio (Android@Home mesh network) device.
@hide -->
<permission android:name="android.permission.LOOP_RADIO"
android:permissionGroup="android.permission-group.NETWORK"
@@ -885,7 +886,7 @@
android:label="@string/permlab_manageAccounts"
android:description="@string/permdesc_manageAccounts" />
- <!-- Allows applications to call into AccountAuthenticators.
+ <!-- @SystemApi Allows applications to call into AccountAuthenticators.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCOUNT_MANAGER"
android:permissionGroup="android.permission-group.ACCOUNTS"
@@ -976,7 +977,7 @@
android:description="@string/permgroupdesc_hardwareControls"
android:priority="260"/>
- <!-- Allows an application to manage preferences and permissions for USB devices
+ <!-- @SystemApi Allows an application to manage preferences and permissions for USB devices
@hide -->
<permission android:name="android.permission.MANAGE_USB"
android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
@@ -984,7 +985,7 @@
android:label="@string/permlab_manageUsb"
android:description="@string/permdesc_manageUsb" />
- <!-- Allows an application to access the MTP USB kernel driver.
+ <!-- @SystemApi Allows an application to access the MTP USB kernel driver.
For use only by the device side MTP implementation.
@hide -->
<permission android:name="android.permission.ACCESS_MTP"
@@ -1020,7 +1021,7 @@
android:description="@string/permdesc_sim_communication"
android:protectionLevel="dangerous" />
- <!-- Allows TvInputService to access underlying TV input hardware such as
+ <!-- @SystemApi Allows TvInputService to access underlying TV input hardware such as
built-in tuners and HDMI-in's.
@hide This should only be used by OEM's TvInputService's.
-->
@@ -1077,7 +1078,7 @@
android:label="@string/permlab_camera"
android:description="@string/permdesc_camera" />
- <!-- Allows disabling the transmit-indicator LED that is normally on when
+ <!-- @SystemApi Allows disabling the transmit-indicator LED that is normally on when
a camera is in use by an application.
@hide -->
<permission android:name="android.permission.CAMERA_DISABLE_TRANSMIT_LED"
@@ -1110,7 +1111,7 @@
android:label="@string/permlab_processOutgoingCalls"
android:description="@string/permdesc_processOutgoingCalls" />
- <!-- Allows modification of the telephony state - power on, mmi, etc.
+ <!-- @SystemApi Allows modification of the telephony state - power on, mmi, etc.
Does not include placing calls.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MODIFY_PHONE_STATE"
@@ -1142,7 +1143,7 @@
android:label="@string/permlab_readPrecisePhoneState"
android:description="@string/permdesc_readPrecisePhoneState" />
- <!-- Allows read access to privileged phone state.
+ <!-- @SystemApi Allows read access to privileged phone state.
@hide Used internally. -->
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
android:permissionGroup="android.permission-group.PHONE_CALLS"
@@ -1165,7 +1166,7 @@
android:description="@string/permdesc_use_sip"
android:label="@string/permlab_use_sip" />
- <!-- Allows an application to request CallHandlerService implementations.
+ <!-- @SystemApi Allows an application to request CallHandlerService implementations.
@hide -->
<permission android:name="android.permission.BIND_CALL_SERVICE"
android:permissionGroup="android.permission-group.PHONE_CALLS"
@@ -1231,7 +1232,7 @@
android:description="@string/permdesc_sdcardWrite"
android:protectionLevel="dangerous" />
- <!-- Allows an application to write to internal media storage
+ <!-- @SystemApi Allows an application to write to internal media storage
@hide -->
<permission android:name="android.permission.WRITE_MEDIA_STORAGE"
android:permissionGroup="android.permission-group.STORAGE"
@@ -1281,14 +1282,15 @@
android:description="@string/permgroupdesc_appInfo"
android:priority="220" />
- <!-- Allows an application to get information about the currently
+ <!-- @SystemApi Allows an application to get information about the currently
or recently running tasks. -->
<permission android:name="android.permission.GET_TASKS"
android:permissionGroup="android.permission-group.APP_INFO"
android:protectionLevel="signature|system"
android:label="@string/permlab_getTasks"
android:description="@string/permdesc_getTasks" />
- <!-- @hide Allows an application to call APIs that allow it to do interactions
+
+ <!-- @SystemApi @hide Allows an application to call APIs that allow it to do interactions
across the users on the device, using singleton services and
user-targeted broadcasts. This permission is not available to
third party applications. -->
@@ -1307,7 +1309,7 @@
android:label="@string/permlab_interactAcrossUsersFull"
android:description="@string/permdesc_interactAcrossUsersFull" />
- <!-- @hide Allows an application to call APIs that allow it to query and manage
+ <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
third party applications. -->
<permission android:name="android.permission.MANAGE_USERS"
@@ -1339,7 +1341,7 @@
android:label="@string/permlab_removeTasks"
android:description="@string/permdesc_removeTasks" />
- <!-- @hide Allows an application to create/manage/remove stacks -->
+ <!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
android:permissionGroup="android.permission-group.APP_INFO"
android:protectionLevel="signature|system"
@@ -1433,7 +1435,7 @@
android:description="@string/permgroupdesc_systemClock"
android:priority="140" />
- <!-- Allows applications to set the system time.
+ <!-- @SystemApi Allows applications to set the system time.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
android:protectionLevel="signature|system"
@@ -1539,7 +1541,7 @@
android:description="@string/permgroupdesc_systemTools"
android:priority="100" />
- <!-- @hide Change the screen compatibility mode of applications -->
+ <!-- @SystemApi @hide Change the screen compatibility mode of applications -->
<permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature"
@@ -1553,7 +1555,7 @@
android:description="@string/permdesc_sdcardAccessAll"
android:protectionLevel="signature" />
- <!-- Allows an application to modify the current configuration, such
+ <!-- @SystemApi Allows an application to modify the current configuration, such
as locale. -->
<permission android:name="android.permission.CHANGE_CONFIGURATION"
android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
@@ -1568,14 +1570,14 @@
android:label="@string/permlab_writeSettings"
android:description="@string/permdesc_writeSettings" />
- <!-- Allows an application to modify the Google service map.
+ <!-- @SystemApi Allows an application to modify the Google service map.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_GSERVICES"
android:protectionLevel="signature|system"
android:label="@string/permlab_writeGservices"
android:description="@string/permdesc_writeGservices" />
- <!-- Allows an application to call
+ <!-- @SystemApi Allows an application to call
{@link android.app.ActivityManager#forceStopPackage}.
@hide -->
<permission android:name="android.permission.FORCE_STOP_PACKAGES"
@@ -1584,7 +1586,7 @@
android:label="@string/permlab_forceStopPackages"
android:description="@string/permdesc_forceStopPackages" />
- <!-- @hide Allows an application to retrieve the content of the active window
+ <!-- @SystemApi @hide Allows an application to retrieve the content of the active window
An active window is the window that has fired an accessibility event. -->
<permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
android:permissionGroup="android.permission-group.PERSONAL_INFO"
@@ -1592,7 +1594,7 @@
android:label="@string/permlab_retrieve_window_content"
android:description="@string/permdesc_retrieve_window_content" />
- <!-- Modify the global animation scaling factor.
+ <!-- @SystemApi Modify the global animation scaling factor.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ANIMATION_SCALE"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -1651,7 +1653,7 @@
android:label="@string/permlab_broadcastSticky"
android:description="@string/permdesc_broadcastSticky" />
- <!-- Allows mounting and unmounting file systems for removable storage.
+ <!-- @SystemApi Allows mounting and unmounting file systems for removable storage.
<p>Not for use by third-party applications.-->
<permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -1659,7 +1661,7 @@
android:label="@string/permlab_mount_unmount_filesystems"
android:description="@string/permdesc_mount_unmount_filesystems" />
- <!-- Allows formatting file systems for removable storage.
+ <!-- @SystemApi Allows formatting file systems for removable storage.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -1707,7 +1709,7 @@
android:label="@string/permlab_asec_rename"
android:description="@string/permdesc_asec_rename" />
- <!-- Allows applications to write the apn settings.
+ <!-- @SystemApi Allows applications to write the apn settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_APN_SETTINGS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -1743,14 +1745,14 @@
android:label="@string/permlab_clearAppCache"
android:description="@string/permdesc_clearAppCache" />
- <!-- Allows an application to use any media decoder when decoding for playback
+ <!-- @SystemApi Allows an application to use any media decoder when decoding for playback
@hide -->
<permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
android:protectionLevel="signature|system"
android:label="@string/permlab_anyCodecForPlayback"
android:description="@string/permdesc_anyCodecForPlayback" />
- <!-- Allows an application to install and/or uninstall CA certificates on
+ <!-- @SystemApi Allows an application to install and/or uninstall CA certificates on
behalf of the user.
@hide -->
<permission android:name="android.permission.MANAGE_CA_CERTIFICATES"
@@ -1758,7 +1760,7 @@
android:label="@string/permlab_manageCaCertificates"
android:description="@string/permdesc_manageCaCertificates" />
- <!-- Allows an application to do certain operations needed for
+ <!-- @SystemApi Allows an application to do certain operations needed for
interacting with the recovery (system update) system. -->
<permission android:name="android.permission.RECOVERY"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -1766,12 +1768,13 @@
android:label="@string/permlab_recovery"
android:description="@string/permdesc_recovery" />
- <!-- Allows the system to bind to an application's idle services
+ <!-- Allows the system to bind to an application's task services
@hide -->
- <permission android:name="android.permission.BIND_IDLE_SERVICE"
+ <permission android:name="android.permission.BIND_JOB_SERVICE"
android:protectionLevel="signature"
- android:label="@string/permlab_bindIdleService"
- android:description="@string/permdesc_bindIdleService" />
+ android:label="@string/permlab_bindJobService"
+ android:description="@string/permdesc_bindJobService" />
+ <uses-permission android:name="android.permission.BIND_JOB_SERVICE"/>
<!-- ========================================= -->
<!-- Permissions for special development tools -->
@@ -1787,7 +1790,7 @@
android:description="@string/permgroupdesc_developmentTools"
android:priority="310" />
- <!-- Allows an application to read or write the secure system settings.
+ <!-- @SystemApi Allows an application to read or write the secure system settings.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WRITE_SECURE_SETTINGS"
android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
@@ -1795,7 +1798,7 @@
android:label="@string/permlab_writeSecureSettings"
android:description="@string/permdesc_writeSecureSettings" />
- <!-- Allows an application to retrieve state dump information from system services.
+ <!-- @SystemApi Allows an application to retrieve state dump information from system services.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DUMP"
android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
@@ -1803,7 +1806,7 @@
android:label="@string/permlab_dump"
android:description="@string/permdesc_dump" />
- <!-- Allows an application to read the low-level system log files.
+ <!-- @SystemApi Allows an application to read the low-level system log files.
<p>Not for use by third-party applications, because
Log entries can contain the user's private information. -->
<permission android:name="android.permission.READ_LOGS"
@@ -1812,7 +1815,7 @@
android:label="@string/permlab_readLogs"
android:description="@string/permdesc_readLogs" />
- <!-- Configure an application for debugging.
+ <!-- @SystemApi Configure an application for debugging.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_DEBUG_APP"
android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
@@ -1820,7 +1823,7 @@
android:label="@string/permlab_setDebugApp"
android:description="@string/permdesc_setDebugApp" />
- <!-- Allows an application to set the maximum number of (not needed)
+ <!-- @SystemApi Allows an application to set the maximum number of (not needed)
application processes that can be running.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_PROCESS_LIMIT"
@@ -1829,7 +1832,7 @@
android:label="@string/permlab_setProcessLimit"
android:description="@string/permdesc_setProcessLimit" />
- <!-- Allows an application to control whether activities are immediately
+ <!-- @SystemApi Allows an application to control whether activities are immediately
finished when put in the background.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_ALWAYS_FINISH"
@@ -1838,7 +1841,7 @@
android:label="@string/permlab_setAlwaysFinish"
android:description="@string/permdesc_setAlwaysFinish" />
- <!-- Allow an application to request that a signal be sent to all persistent processes.
+ <!-- @SystemApi Allow an application to request that a signal be sent to all persistent processes.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
@@ -1851,7 +1854,7 @@
<!-- ==================================== -->
<eat-comment />
- <!-- Allows applications to RW to diagnostic resources.
+ <!-- @SystemApi Allows applications to RW to diagnostic resources.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DIAGNOSTIC"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -1859,7 +1862,7 @@
android:description="@string/permdesc_diagnostic"
android:label="@string/permlab_diagnostic" />
- <!-- Allows an application to open, close, or disable the status bar
+ <!-- @SystemApi Allows an application to open, close, or disable the status bar
and its icons.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.STATUS_BAR"
@@ -1882,21 +1885,21 @@
android:description="@string/permdesc_forceBack"
android:protectionLevel="signature" />
- <!-- Allows an application to update device statistics.
+ <!-- @SystemApi Allows an application to update device statistics.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.UPDATE_DEVICE_STATS"
android:label="@string/permlab_updateBatteryStats"
android:description="@string/permdesc_updateBatteryStats"
android:protectionLevel="signature|system" />
- <!-- @hide Allows an application to collect battery statistics -->
+ <!-- @SystemApi @hide Allows an application to collect battery statistics -->
<permission android:name="android.permission.GET_APP_OPS_STATS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:label="@string/permlab_getAppOpsStats"
android:description="@string/permdesc_getAppOpsStats"
android:protectionLevel="signature|system|development" />
- <!-- Allows an application to update application operation statistics. Not for
+ <!-- @SystemApi Allows an application to update application operation statistics. Not for
use by third party apps. @hide -->
<permission android:name="android.permission.UPDATE_APP_OPS_STATS"
android:label="@string/permlab_updateAppOpsStats"
@@ -1969,7 +1972,7 @@
android:description="@string/permdesc_runSetActivityWatcher"
android:protectionLevel="signature" />
- <!-- Allows an application to call the activity manager shutdown() API
+ <!-- @SystemApi Allows an application to call the activity manager shutdown() API
to put the higher-level system there into a shutdown state.
@hide -->
<permission android:name="android.permission.SHUTDOWN"
@@ -1977,7 +1980,7 @@
android:description="@string/permdesc_shutdown"
android:protectionLevel="signature|system" />
- <!-- Allows an application to tell the activity manager to temporarily
+ <!-- @SystemApi Allows an application to tell the activity manager to temporarily
stop application switches, putting it into a special mode that
prevents applications from immediately switching away from some
critical UI such as the home screen.
@@ -2068,6 +2071,14 @@
android:description="@string/permdesc_bindVoiceInteraction"
android:protectionLevel="signature" />
+ <!-- Must be required by hotword enrollment application,
+ to ensure that only the system can interact with it.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES"
+ android:label="@string/permlab_manageVoiceKeyphrases"
+ android:description="@string/permdesc_manageVoiceKeyphrases"
+ android:protectionLevel="signature|system" />
+
<!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider},
to ensure that only the system can bind to it.
@hide -->
@@ -2076,7 +2087,7 @@
android:description="@string/permdesc_bindRemoteDisplay"
android:protectionLevel="signature" />
- <!-- Must be required by a {@link android.tv.TvInputService}
+ <!-- Must be required by a {@link android.media.tv.TvInputService}
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_TV_INPUT"
android:label="@string/permlab_bindTvInput"
@@ -2098,7 +2109,7 @@
android:description="@string/permdesc_bindDeviceAdmin"
android:protectionLevel="signature" />
- <!-- Required to add or remove another application as a device admin.
+ <!-- @SystemApi Required to add or remove another application as a device admin.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
@@ -2137,7 +2148,7 @@
android:description="@string/permdesc_setKeyboardLayout"
android:protectionLevel="signature" />
- <!-- Allows an application to install packages.
+ <!-- @SystemApi Allows an application to install packages.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.INSTALL_PACKAGES"
android:label="@string/permlab_installPackages"
@@ -2151,28 +2162,28 @@
android:description="@string/permdesc_clearAppUserData"
android:protectionLevel="signature" />
- <!-- Allows an application to delete cache files.
+ <!-- @SystemApi Allows an application to delete cache files.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DELETE_CACHE_FILES"
android:label="@string/permlab_deleteCacheFiles"
android:description="@string/permdesc_deleteCacheFiles"
android:protectionLevel="signature|system" />
- <!-- Allows an application to delete packages.
+ <!-- @SystemApi Allows an application to delete packages.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.DELETE_PACKAGES"
android:label="@string/permlab_deletePackages"
android:description="@string/permdesc_deletePackages"
android:protectionLevel="signature|system" />
- <!-- Allows an application to move location of installed package.
+ <!-- @SystemApi Allows an application to move location of installed package.
@hide -->
<permission android:name="android.permission.MOVE_PACKAGE"
android:label="@string/permlab_movePackage"
android:description="@string/permdesc_movePackage"
android:protectionLevel="signature|system" />
- <!-- Allows an application to change whether an application component (other than its own) is
+ <!-- @SystemApi Allows an application to change whether an application component (other than its own) is
enabled or not.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
@@ -2193,7 +2204,7 @@
android:description="@string/permdesc_accessSurfaceFlinger"
android:protectionLevel="signature" />
- <!-- Allows an application to take screen shots and more generally
+ <!-- @SystemApi Allows an application to take screen shots and more generally
get access to the frame buffer data.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_FRAME_BUFFER"
@@ -2202,7 +2213,7 @@
android:protectionLevel="signature|system" />
<!-- Allows an application to use InputFlinger's low level features.
- <p>Not for use by third-party applications. -->
+ @hide -->
<permission android:name="android.permission.ACCESS_INPUT_FLINGER"
android:label="@string/permlab_accessInputFlinger"
android:description="@string/permdesc_accessInputFlinger"
@@ -2224,14 +2235,14 @@
android:description="@string/permdesc_controlWifiDisplay"
android:protectionLevel="signature" />
- <!-- Allows an application to capture audio output.
+ <!-- @SystemApi Allows an application to capture audio output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
android:label="@string/permlab_captureAudioOutput"
android:description="@string/permdesc_captureAudioOutput"
android:protectionLevel="signature|system" />
- <!-- Allows an application to capture audio for hotword detection.
+ <!-- @SystemApi Allows an application to capture audio for hotword detection.
<p>Not for use by third-party applications.</p>
@hide -->
<permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
@@ -2239,21 +2250,29 @@
android:description="@string/permdesc_captureAudioHotword"
android:protectionLevel="signature|system" />
- <!-- Allows an application to capture video output.
+ <!-- @SystemApi Allows an application to modify audio routing and override policy decisions.
+ <p>Not for use by third-party applications.</p>
+ @hide -->
+ <permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
+ android:label="@string/permlab_modifyAudioRouting"
+ android:description="@string/permdesc_modifyAudioRouting"
+ android:protectionLevel="signature|system" />
+
+ <!-- @SystemApi Allows an application to capture video output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
android:label="@string/permlab_captureVideoOutput"
android:description="@string/permdesc_captureVideoOutput"
android:protectionLevel="signature|system" />
- <!-- Allows an application to capture secure video output.
+ <!-- @SystemApi Allows an application to capture secure video output.
<p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
android:label="@string/permlab_captureSecureVideoOutput"
android:description="@string/permdesc_captureSecureVideoOutput"
android:protectionLevel="signature|system" />
- <!-- Allows an application to know what content is playing and control its playback.
+ <!-- @SystemApi Allows an application to know what content is playing and control its playback.
<p>Not for use by third-party applications due to privacy of media consumption</p> -->
<permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
android:label="@string/permlab_mediaContentControl"
@@ -2267,7 +2286,7 @@
android:description="@string/permdesc_brick"
android:protectionLevel="signature" />
- <!-- Required to be able to reboot the device.
+ <!-- @SystemApi Required to be able to reboot the device.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.REBOOT"
android:label="@string/permlab_reboot"
@@ -2319,20 +2338,20 @@
android:description="@string/permdesc_broadcastWapPush"
android:protectionLevel="signature" />
- <!-- Allows an application to broadcast a SCORE_NETWORKS request.
+ <!-- @SystemApi Allows an application to broadcast a SCORE_NETWORKS request.
<p>Not for use by third-party applications. @hide -->
<permission android:name="android.permission.BROADCAST_SCORE_NETWORKS"
android:label="@string/permlab_broadcastScoreNetworks"
android:description="@string/permdesc_broadcastScoreNetworks"
android:protectionLevel="signature|system" />
- <!-- Not for use by third-party applications. -->
+ <!-- @SystemApi Not for use by third-party applications. -->
<permission android:name="android.permission.MASTER_CLEAR"
android:label="@string/permlab_masterClear"
android:description="@string/permdesc_masterClear"
android:protectionLevel="signature|system" />
- <!-- Allows an application to call any phone number, including emergency
+ <!-- @SystemApi Allows an application to call any phone number, including emergency
numbers, without going through the Dialer user interface for the user
to confirm the call being placed.
<p>Not for use by third-party applications. -->
@@ -2341,13 +2360,13 @@
android:description="@string/permdesc_callPrivileged"
android:protectionLevel="signature|system" />
- <!-- Allows an application to perform CDMA OTA provisioning @hide -->
+ <!-- @SystemApi Allows an application to perform CDMA OTA provisioning @hide -->
<permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
android:label="@string/permlab_performCdmaProvisioning"
android:description="@string/permdesc_performCdmaProvisioning"
android:protectionLevel="signature|system" />
- <!-- Allows enabling/disabling location update notifications from
+ <!-- @SystemApi Allows enabling/disabling location update notifications from
the radio.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
@@ -2355,7 +2374,7 @@
android:description="@string/permdesc_locationUpdates"
android:protectionLevel="signature|system" />
- <!-- Allows read/write access to the "properties" table in the checkin
+ <!-- @SystemApi Allows read/write access to the "properties" table in the checkin
database, to change values that get uploaded.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
@@ -2363,21 +2382,21 @@
android:description="@string/permdesc_checkinProperties"
android:protectionLevel="signature|system" />
- <!-- Allows an application to collect component usage
+ <!-- @SystemApi Allows an application to collect component usage
statistics @hide -->
<permission android:name="android.permission.PACKAGE_USAGE_STATS"
android:label="@string/permlab_pkgUsageStats"
android:description="@string/permdesc_pkgUsageStats"
android:protectionLevel="signature|system" />
- <!-- Allows an application to collect battery statistics -->
+ <!-- @SystemApi Allows an application to collect battery statistics -->
<permission android:name="android.permission.BATTERY_STATS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:label="@string/permlab_batteryStats"
android:description="@string/permdesc_batteryStats"
android:protectionLevel="signature|system" />
- <!-- Allows an application to control the backup and restore process.
+ <!-- @SystemApi Allows an application to control the backup and restore process.
<p>Not for use by third-party applications.
@hide pending API council -->
<permission android:name="android.permission.BACKUP"
@@ -2393,14 +2412,14 @@
android:description="@string/permdesc_confirm_full_backup"
android:protectionLevel="signature" />
- <!-- Must be required by a {@link android.widget.RemoteViewsService},
+ <!-- @SystemApi Must be required by a {@link android.widget.RemoteViewsService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_REMOTEVIEWS"
android:label="@string/permlab_bindRemoteViews"
android:description="@string/permdesc_bindRemoteViews"
android:protectionLevel="signature|system" />
- <!-- Allows an application to tell the AppWidget service which application
+ <!-- @SystemApi Allows an application to tell the AppWidget service which application
can access AppWidget's data. The normal user flow is that a user
picks an AppWidget to go into a particular host, thereby giving that
host application access to the private data from the AppWidget app.
@@ -2412,14 +2431,14 @@
android:description="@string/permdesc_bindGadget"
android:protectionLevel="signature|system" />
- <!-- Private permission, to restrict who can bring up a dialog to add a new
+ <!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
keyguard widget
@hide -->
<permission android:name="android.permission.BIND_KEYGUARD_APPWIDGET"
android:permissionGroup="android.permission-group.PERSONAL_INFO"
android:protectionLevel="signature|system" />
- <!-- Internal permission allowing an application to query/set which
+ <!-- @SystemApi Internal permission allowing an application to query/set which
applications can bind AppWidgets.
@hide -->
<permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"
@@ -2435,7 +2454,7 @@
android:description="@string/permdesc_changeBackgroundDataSetting"
android:label="@string/permlab_changeBackgroundDataSetting" />
- <!-- This permission can be used on content providers to allow the global
+ <!-- @SystemApi This permission can be used on content providers to allow the global
search system to access their data. Typically it used when the
provider has some permissions protecting it (which global search
would not be expected to hold), and added as a read-only permission
@@ -2458,32 +2477,32 @@
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature" />
- <!-- Internal permission to allows an application to read indexable data.
+ <!-- @SystemApi Internal permission to allows an application to read indexable data.
@hide -->
<permission android:name="android.permission.READ_SEARCH_INDEXABLES"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature|system" />
- <!-- Allows applications to set a live wallpaper.
+ <!-- @SystemApi Allows applications to set a live wallpaper.
@hide XXX Change to signature once the picker is moved to its
own apk as Ghod Intended. -->
<permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature|system" />
- <!-- Allows applications to read dream settings and dream state.
+ <!-- @SystemApi Allows applications to read dream settings and dream state.
@hide -->
<permission android:name="android.permission.READ_DREAM_STATE"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature|system" />
- <!-- Allows applications to write dream settings, and start or stop dreaming.
+ <!-- @SystemApi Allows applications to write dream settings, and start or stop dreaming.
@hide -->
<permission android:name="android.permission.WRITE_DREAM_STATE"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature|system" />
- <!-- Allow an application to read and write the cache partition.
+ <!-- @SystemApi Allow an application to read and write the cache partition.
@hide -->
<permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
android:label="@string/permlab_cache_filesystem"
@@ -2500,13 +2519,13 @@
android:description="@string/permlab_copyProtectedData"
android:protectionLevel="signature" />
- <!-- Internal permission protecting access to the encryption methods
+ <!-- @SystemApi Internal permission protecting access to the encryption methods
@hide
-->
<permission android:name="android.permission.CRYPT_KEEPER"
android:protectionLevel="signature|system" />
- <!-- Allows an application to read historical network usage for
+ <!-- @SystemApi Allows an application to read historical network usage for
specific networks and applications. @hide -->
<permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
android:label="@string/permlab_readNetworkUsageHistory"
@@ -2520,7 +2539,7 @@
android:description="@string/permdesc_manageNetworkPolicy"
android:protectionLevel="signature" />
- <!-- Allows an application to account its network traffic against other UIDs. Used
+ <!-- @SystemApi Allows an application to account its network traffic against other UIDs. Used
by system services like download manager and media server. Not for use by
third party apps. @hide -->
<permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
@@ -2528,7 +2547,7 @@
android:description="@string/permdesc_modifyNetworkAccounting"
android:protectionLevel="signature|system" />
- <!-- Allows an application to mark traffic as from another user for per user routing.
+ <!-- @SystemApi Allows an application to mark traffic as from another user for per user routing.
Used by system wide services like media server that execute delegated network connections
for users.
@hide
@@ -2545,7 +2564,7 @@
android:protectionLevel="signature" />
<uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/>
- <!-- @hide Package verifier needs to have this permission before the PackageManager will
+ <!-- @SystemApi @hide Package verifier needs to have this permission before the PackageManager will
trust it to verify packages.
-->
<permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
@@ -2562,7 +2581,7 @@
android:description="@string/permdesc_bindPackageVerifier"
android:protectionLevel="signature" />
- <!-- Allows applications to access serial ports via the SerialManager.
+ <!-- @SystemApi Allows applications to access serial ports via the SerialManager.
@hide -->
<permission android:name="android.permission.SERIAL_PORT"
android:label="@string/permlab_serialPort"
@@ -2579,7 +2598,8 @@
android:label="@string/permlab_accessContentProvidersExternally"
android:description="@string/permdesc_accessContentProvidersExternally"
android:protectionLevel="signature" />
- <!-- Allows an application to hold an UpdateLock, recommending that a headless
+
+ <!-- @SystemApi Allows an application to hold an UpdateLock, recommending that a headless
OTA reboot *not* occur while the lock is held.
@hide -->
<permission android:name="android.permission.UPDATE_LOCK"
@@ -2587,7 +2607,7 @@
android:description="@string/permdesc_updateLock"
android:protectionLevel="signatureOrSystem" />
- <!-- Allows an application to read the current set of notifications, including
+ <!-- @SystemApi Allows an application to read the current set of notifications, including
any metadata and intents attached.
@hide -->
<permission android:name="android.permission.ACCESS_NOTIFICATIONS"
@@ -2616,6 +2636,13 @@
android:label="@string/permlab_trust_listener"
android:description="@string/permdesc_trust_listener" />
+ <!-- @SystemApi Allows an application to provide a trust agent.
+ @hide For security reasons, this is a platform-only permission. -->
+ <permission android:name="android.permission.PROVIDE_TRUST_AGENT"
+ android:protectionLevel="signatureOrSystem"
+ android:label="@string/permlab_provide_trust_agent"
+ android:description="@string/permdesc_provide_trust_agent" />
+
<!-- Must be required by an {@link
android.service.trust.TrustAgentService},
to ensure that only the system can bind to it. -->
@@ -2641,7 +2668,7 @@
android:description="@string/permdesc_bindConditionProviderService"
android:protectionLevel="signature" />
- <!-- Allows an application to call into a carrier setup flow. It is up to the
+ <!-- @SystemApi Allows an application to call into a carrier setup flow. It is up to the
carrier setup application to enforce that this permission is required
@hide This is not a third-party API (intended for OEMs and system apps). -->
<permission android:name="android.permission.INVOKE_CARRIER_SETUP"
@@ -2649,14 +2676,14 @@
android:description="@string/permdesc_invokeCarrierSetup"
android:protectionLevel="signature|system" />
- <!-- Allows an application to listen for network condition observations.
+ <!-- @SystemApi Allows an application to listen for network condition observations.
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.ACCESS_NETWORK_CONDITIONS"
android:label="@string/permlab_accessNetworkConditions"
android:description="@string/permdesc_accessNetworkConditions"
android:protectionLevel="signature|system" />
- <!-- Allows an application to provision and access DRM certificates
+ <!-- @SystemApi Allows an application to provision and access DRM certificates
@hide This is not a third-party API (intended for system apps). -->
<permission android:name="android.permission.ACCESS_DRM_CERTIFICATES"
android:label="@string/permlab_accessDrmCertificates"
@@ -2680,7 +2707,8 @@
android:theme="@style/Theme.Holo.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true"
- android:multiprocess="true">
+ android:multiprocess="true"
+ android:documentLaunchMode="never">
<intent-filter>
<action android:name="android.intent.action.CHOOSER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -2702,7 +2730,7 @@
</activity-alias>
<activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
android:targetActivity="com.android.internal.app.IntentForwarderActivity"
- android:icon="@drawable/work_icon"
+ android:icon="@drawable/ic_afw_icon"
android:exported="true"
android:label="@string/managed_profile_label">
</activity-alias>
@@ -2786,7 +2814,7 @@
</activity>
<activity android:name="com.android.internal.app.RestrictionsPinActivity"
- android:theme="@style/Theme.Holo.Dialog.Alert"
+ android:theme="@style/Theme.Material.Light.Dialog.Alert"
android:excludeFromRecents="true"
android:windowSoftInputMode="adjustPan"
android:process=":ui">
@@ -2869,11 +2897,8 @@
</service>
<service android:name="com.android.server.MountServiceIdler"
- android:exported="false"
- android:permission="android.permission.BIND_IDLE_SERVICE" >
- <intent-filter>
- <action android:name="android.service.idle.IdleService" />
- </intent-filter>
+ android:exported="true"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
</application>
diff --git a/core/res/assets/images/android-logo-mask.png b/core/res/assets/images/android-logo-mask.png
index 1e8ab95..3078b28 100644
--- a/core/res/assets/images/android-logo-mask.png
+++ b/core/res/assets/images/android-logo-mask.png
Binary files differ
diff --git a/core/res/assets/images/android-logo-shine.png b/core/res/assets/images/android-logo-shine.png
index 60144f1..add6796 100644
--- a/core/res/assets/images/android-logo-shine.png
+++ b/core/res/assets/images/android-logo-shine.png
Binary files differ
diff --git a/core/res/res/anim-land/task_close_enter.xml b/core/res/res/anim-land/task_close_enter.xml
deleted file mode 100644
index facc42b..0000000
--- a/core/res/res/anim-land/task_close_enter.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
-
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="300"
- android:duration="400"/>
-
- <translate android:fromXDelta="-120%" android:toXDelta="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="300"
- android:duration="400" />
-
- <scale android:fromXScale=".5" android:toXScale="1.0"
- android:fromYScale=".5" android:toYScale="1.0"
- android:pivotY="50%p" android:pivotX="0%p"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="300"
- android:duration="400" />
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim-land/task_close_exit.xml b/core/res/res/anim-land/task_close_exit.xml
deleted file mode 100644
index e104c33..0000000
--- a/core/res/res/anim-land/task_close_exit.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
-
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="300"/>
-
- <translate android:fromXDelta="0" android:toXDelta="120%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_cubic"
- android:duration="300"/>
-
- <scale android:fromXScale="1.0" android:toXScale="0.5"
- android:fromYScale="1.0" android:toYScale="0.5"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:pivotY="50%p" android:pivotX="100%p"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="300" />
-
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:duration="700" />
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim-land/task_open_enter.xml b/core/res/res/anim-land/task_open_enter.xml
deleted file mode 100644
index dc7c7a9..0000000
--- a/core/res/res/anim-land/task_open_enter.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
-
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="300"
- android:duration="400"/>
-
- <translate android:fromXDelta="120%" android:toXDelta="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="300"
- android:duration="400" />
-
- <scale android:fromXScale=".5" android:toXScale="1.0"
- android:fromYScale=".5" android:toYScale="1.0"
- android:pivotY="50%p" android:pivotX="100%p"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="300"
- android:duration="400" />
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim-land/task_open_exit.xml b/core/res/res/anim-land/task_open_exit.xml
deleted file mode 100644
index 701afa6..0000000
--- a/core/res/res/anim-land/task_open_exit.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
-
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="300"/>
-
- <translate android:fromXDelta="0" android:toXDelta="-120%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_cubic"
- android:duration="300"/>
-
- <scale android:fromXScale="1.0" android:toXScale="0.5"
- android:fromYScale="1.0" android:toYScale="0.5"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:pivotY="50%p" android:pivotX="0%p"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="300" />
-
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:duration="700" />
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim-sw720dp/task_close_enter.xml b/core/res/res/anim-sw720dp/task_close_enter.xml
deleted file mode 100644
index e25978b..0000000
--- a/core/res/res/anim-sw720dp/task_close_enter.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
-
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="500"
- android:duration="400"/>
-
- <translate android:fromYDelta="-50%" android:toYDelta="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="500"
- android:duration="400" />
-
- <scale android:fromXScale=".8" android:toXScale="1.0"
- android:fromYScale=".8" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="0%p"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="500"
- android:duration="400" />
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim-sw720dp/task_close_exit.xml b/core/res/res/anim-sw720dp/task_close_exit.xml
deleted file mode 100644
index 2d7e2a6..0000000
--- a/core/res/res/anim-sw720dp/task_close_exit.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
-
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="350"/>
-
- <translate android:fromYDelta="0" android:toYDelta="50%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_cubic"
- android:duration="350"/>
-
- <scale android:fromXScale="1.0" android:toXScale="0.8"
- android:fromYScale="1.0" android:toYScale="0.8"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:pivotX="50%p" android:pivotY="100%p"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="350" />
-
- <!-- This is needed to keep the animation running while task_close_enter completes -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:duration="900" />
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim-sw720dp/task_open_enter.xml b/core/res/res/anim-sw720dp/task_open_enter.xml
deleted file mode 100644
index d583b6e..0000000
--- a/core/res/res/anim-sw720dp/task_open_enter.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
-
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="500"
- android:duration="400"/>
-
- <translate android:fromYDelta="50%" android:toYDelta="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:startOffset="500"
- android:duration="400" />
-
- <scale android:fromXScale=".8" android:toXScale="1.0"
- android:fromYScale=".8" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="100%p"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="500"
- android:duration="400" />
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim-sw720dp/task_open_exit.xml b/core/res/res/anim-sw720dp/task_open_exit.xml
deleted file mode 100644
index dd25a0c..0000000
--- a/core/res/res/anim-sw720dp/task_open_exit.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
-
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="350"/>
-
- <translate android:fromYDelta="0" android:toYDelta="-50%"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_cubic"
- android:duration="350"/>
-
- <scale android:fromXScale="1.0" android:toXScale="0.8"
- android:fromYScale="1.0" android:toYScale="0.8"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:pivotX="50%p" android:pivotY="0%p"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="350" />
-
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
- android:duration="900" />
-</set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index 84e4a19..a67b0ca 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -18,7 +18,8 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android" android:zAdjustment="normal">
- <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+ <alpha android:fromAlpha="0.7" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:duration="300"/>
+ android:interpolator="@interpolator/linear_out_slow_in"
+ android:duration="250"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index 32f6d38..52c3adf 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -20,13 +20,13 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" android:zAdjustment="top">
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:interpolator="@interpolator/decelerate_cubic"
+ android:interpolator="@interpolator/accelerate_quart"
+ android:fillEnabled="true"
+ android:fillBefore="false" android:fillAfter="true"
+ android:startOffset="100"
+ android:duration="150"/>
+ <translate android:fromYDelta="0%" android:toYDelta="5%"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:duration="300"/>
- <scale android:fromXScale="1.0" android:toXScale=".8"
- android:fromYScale="1.0" android:toYScale=".8"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@interpolator/decelerate_cubic"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:duration="300"/>
+ android:interpolator="@interpolator/accelerate_quint"
+ android:duration="250"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index d553bdf..1d949d2 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -21,15 +21,12 @@
android:shareInterpolator="false"
android:zAdjustment="top">
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
- android:interpolator="@interpolator/decelerate_cubic"
+ android:interpolator="@interpolator/decelerate_quart"
android:fillEnabled="true"
android:fillBefore="false" android:fillAfter="true"
- android:duration="300"/>
- <scale android:fromXScale=".8" android:toXScale="1.0"
- android:fromYScale=".8" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@interpolator/decelerate_cubic"
- android:fillEnabled="true"
- android:fillBefore="false" android:fillAfter="true"
- android:duration="300"/>
+ android:duration="200"/>
+ <translate android:fromYDelta="8%" android:toYDelta="0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:duration="350"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index 2d105d1..3a84197 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -19,8 +19,8 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff000000" android:zAdjustment="normal">
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.7"
android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quint"
- android:duration="300"/>
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="217"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/button_state_list_anim_quantum.xml b/core/res/res/anim/button_state_list_anim_material.xml
similarity index 100%
rename from core/res/res/anim/button_state_list_anim_quantum.xml
rename to core/res/res/anim/button_state_list_anim_material.xml
diff --git a/core/res/res/anim/popup_enter_quantum.xml b/core/res/res/anim/popup_enter_material.xml
similarity index 100%
rename from core/res/res/anim/popup_enter_quantum.xml
rename to core/res/res/anim/popup_enter_material.xml
diff --git a/core/res/res/anim/popup_exit_quantum.xml b/core/res/res/anim/popup_exit_material.xml
similarity index 100%
rename from core/res/res/anim/popup_exit_quantum.xml
rename to core/res/res/anim/popup_exit_material.xml
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index 9a747a1..b07f470 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -18,25 +18,25 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+ android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
- <alpha android:fromAlpha="0" android:toAlpha="1.0"
+ <alpha android:fromAlpha="0.6" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="300"
- android:duration="400"/>
+ android:interpolator="@interpolator/decelerate_cubic"
+ android:startOffset="600"
+ android:duration="133"/>
- <translate android:fromYDelta="-120%" android:toYDelta="0"
+ <translate android:fromYDelta="10%" android:toYDelta="0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quint"
+ android:interpolator="@interpolator/decelerate_cubic"
android:startOffset="300"
- android:duration="400" />
+ android:duration="433" />
- <scale android:fromXScale=".5" android:toXScale="1.0"
- android:fromYScale=".5" android:toYScale="1.0"
+ <scale android:fromXScale=".9" android:toXScale="1.0"
+ android:fromYScale=".9" android:toYScale="1.0"
android:pivotX="50%p" android:pivotY="0%p"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
+ android:interpolator="@interpolator/fast_out_slow_in"
android:startOffset="300"
- android:duration="400" />
+ android:duration="433" />
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index 35b1aa3..d23c74f 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -18,24 +18,18 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+ android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
<alpha android:fromAlpha="1.0" android:toAlpha="0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/accelerate_quad"
- android:duration="300"/>
+ android:startOffset="250"
+ android:duration="167"/>
- <translate android:fromYDelta="0" android:toYDelta="120%"
+ <translate android:fromYDelta="0" android:toYDelta="110%"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_cubic"
- android:duration="300"/>
-
- <scale android:fromXScale="1.0" android:toXScale="0.5"
- android:fromYScale="1.0" android:toYScale="0.5"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:pivotX="50%p" android:pivotY="100%p"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="300" />
+ android:interpolator="@interpolator/accelerate_quint"
+ android:duration="417"/>
<!-- This is needed to keep the animation running while task_open_enter completes -->
<alpha android:fromAlpha="1.0" android:toAlpha="1.0"
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 5e4ae18..8321ea4 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -22,21 +22,13 @@
<alpha android:fromAlpha="0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
+ android:interpolator="@interpolator/decelerate_quart"
android:startOffset="300"
- android:duration="400"/>
+ android:duration="167"/>
- <translate android:fromYDelta="120%" android:toYDelta="0"
+ <translate android:fromYDelta="110%" android:toYDelta="0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/decelerate_quint"
android:startOffset="300"
- android:duration="400" />
-
- <scale android:fromXScale=".5" android:toXScale="1.0"
- android:fromYScale=".5" android:toYScale="1.0"
- android:pivotX="50%p" android:pivotY="100%p"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/decelerate_quad"
- android:startOffset="300"
- android:duration="400" />
+ android:duration="417" />
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 0ba35d7..78d0fb0 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -20,22 +20,22 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="300"/>
-
- <translate android:fromYDelta="0" android:toYDelta="-120%"
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.6"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
android:interpolator="@interpolator/accelerate_cubic"
- android:duration="300"/>
+ android:duration="133"/>
- <scale android:fromXScale="1.0" android:toXScale="0.5"
- android:fromYScale="1.0" android:toYScale="0.5"
+ <translate android:fromYDelta="0" android:toYDelta="10%"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:pivotX="50%p" android:pivotY="0%p"
- android:interpolator="@interpolator/accelerate_quad"
- android:duration="300" />
+ android:interpolator="@interpolator/accelerate_cubic"
+ android:duration="433"/>
+
+ <scale android:fromXScale="1.0" android:toXScale="0.9"
+ android:fromYScale="1.0" android:toYScale="0.9"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="433" />
<!-- This is needed to keep the animation running while task_open_enter completes -->
<alpha android:fromAlpha="1.0" android:toAlpha="1.0"
diff --git a/core/res/res/anim/wallpaper_open_enter.xml b/core/res/res/anim/wallpaper_open_enter.xml
index 12a1bdf..e70f0e7 100644
--- a/core/res/res/anim/wallpaper_open_enter.xml
+++ b/core/res/res/anim/wallpaper_open_enter.xml
@@ -21,5 +21,5 @@
android:detachWallpaper="true" android:shareInterpolator="false" android:zAdjustment="normal">
<alpha android:fromAlpha="1.0" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:duration="375"/>
+ android:duration="225"/>
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml
index b0f97d1..696912b 100644
--- a/core/res/res/anim/wallpaper_open_exit.xml
+++ b/core/res/res/anim/wallpaper_open_exit.xml
@@ -19,14 +19,22 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" android:zAdjustment="top">
- <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
- android:interpolator="@interpolator/accelerate_decelerate"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:duration="200" />
- <scale android:fromXScale="1.0" android:toXScale="0.5"
- android:fromYScale="1.0" android:toYScale="0.5"
- android:pivotX="50%p" android:pivotY="50%p"
- android:interpolator="@interpolator/decelerate_quad"
- android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
- android:duration="375" />
+
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/accelerate_quad"
+ android:startOffset="250"
+ android:duration="167"/>
+
+ <translate android:fromYDelta="0" android:toYDelta="110%"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_linear_in"
+ android:duration="225"/>
+
+ <scale android:fromXScale="1.0" android:toXScale="1.0"
+ android:fromYScale="1.0" android:toYScale="1.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="225" />
</set>
\ No newline at end of file
diff --git a/core/res/res/animator/fragment_open_enter.xml b/core/res/res/animator/fragment_open_enter.xml
index 8cd0e4e..4ae24b9e0 100644
--- a/core/res/res/animator/fragment_open_enter.xml
+++ b/core/res/res/animator/fragment_open_enter.xml
@@ -19,13 +19,13 @@
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:interpolator="@interpolator/decelerate_cubic"
- android:valueFrom="0.8" android:valueTo="1.0"
+ android:valueFrom="0.95" android:valueTo="1.0"
android:valueType="floatType"
android:propertyName="scaleY"
android:duration="300"/>
<objectAnimator
android:interpolator="@interpolator/decelerate_cubic"
- android:valueFrom="0.8" android:valueTo="1.0"
+ android:valueFrom="0.95" android:valueTo="1.0"
android:valueType="floatType"
android:propertyName="scaleX"
android:duration="300"/>
diff --git a/core/res/res/color/background_cache_hint_selector_material_dark.xml b/core/res/res/color/background_cache_hint_selector_material_dark.xml
new file mode 100644
index 0000000..e7c9426
--- /dev/null
+++ b/core/res/res/color/background_cache_hint_selector_material_dark.xml
@@ -0,0 +1,20 @@
+<?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_accelerated="false" android:color="@android:color/background_material_dark" />
+ <item android:color="@android:color/transparent" />
+</selector>
diff --git a/core/res/res/color/background_cache_hint_selector_material_light.xml b/core/res/res/color/background_cache_hint_selector_material_light.xml
new file mode 100644
index 0000000..96f1f59
--- /dev/null
+++ b/core/res/res/color/background_cache_hint_selector_material_light.xml
@@ -0,0 +1,20 @@
+<?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_accelerated="false" android:color="@android:color/background_material_light" />
+ <item android:color="@android:color/transparent" />
+</selector>
diff --git a/core/res/res/color/background_cache_hint_selector_quantum_dark.xml b/core/res/res/color/background_cache_hint_selector_quantum_dark.xml
deleted file mode 100644
index ab66501..0000000
--- a/core/res/res/color/background_cache_hint_selector_quantum_dark.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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_accelerated="false" android:color="@android:color/background_quantum_dark" />
- <item android:color="@android:color/transparent" />
-</selector>
diff --git a/core/res/res/color/background_cache_hint_selector_quantum_light.xml b/core/res/res/color/background_cache_hint_selector_quantum_light.xml
deleted file mode 100644
index fb940a9..0000000
--- a/core/res/res/color/background_cache_hint_selector_quantum_light.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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_accelerated="false" android:color="@android:color/background_quantum_light" />
- <item android:color="@android:color/transparent" />
-</selector>
diff --git a/core/res/res/color/btn_default_material_dark.xml b/core/res/res/color/btn_default_material_dark.xml
new file mode 100644
index 0000000..59555ae
--- /dev/null
+++ b/core/res/res/color/btn_default_material_dark.xml
@@ -0,0 +1,20 @@
+<?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_enabled="false" android:alpha="0.5" android:color="@color/button_material_dark"/>
+ <item android:color="@color/button_material_dark"/>
+</selector>
diff --git a/core/res/res/color/btn_default_material_light.xml b/core/res/res/color/btn_default_material_light.xml
new file mode 100644
index 0000000..6511a22
--- /dev/null
+++ b/core/res/res/color/btn_default_material_light.xml
@@ -0,0 +1,20 @@
+<?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_enabled="false" android:alpha="0.5" android:color="@color/button_material_light"/>
+ <item android:color="@color/button_material_light"/>
+</selector>
diff --git a/core/res/res/color/btn_default_quantum_dark.xml b/core/res/res/color/btn_default_quantum_dark.xml
deleted file mode 100644
index ec0f140..0000000
--- a/core/res/res/color/btn_default_quantum_dark.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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_enabled="false" android:alpha="0.5" android:color="@color/button_quantum_dark"/>
- <item android:color="@color/button_quantum_dark"/>
-</selector>
diff --git a/core/res/res/color/btn_default_quantum_light.xml b/core/res/res/color/btn_default_quantum_light.xml
deleted file mode 100644
index 9536d24..0000000
--- a/core/res/res/color/btn_default_quantum_light.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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_enabled="false" android:alpha="0.5" android:color="@color/button_quantum_light"/>
- <item android:color="@color/button_quantum_light"/>
-</selector>
diff --git a/core/res/res/color/primary_text_disable_only_material_dark.xml b/core/res/res/color/primary_text_disable_only_material_dark.xml
new file mode 100644
index 0000000..cf7acaa
--- /dev/null
+++ b/core/res/res/color/primary_text_disable_only_material_dark.xml
@@ -0,0 +1,20 @@
+<?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_enabled="false" android:alpha="0.5" android:color="@android:color/bright_foreground_material_dark"/>
+ <item android:color="@android:color/bright_foreground_material_dark"/>
+</selector>
diff --git a/core/res/res/color/primary_text_disable_only_material_light.xml b/core/res/res/color/primary_text_disable_only_material_light.xml
new file mode 100644
index 0000000..bf5d2c0
--- /dev/null
+++ b/core/res/res/color/primary_text_disable_only_material_light.xml
@@ -0,0 +1,20 @@
+<?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_enabled="false" android:alpha="0.5" android:color="@android:color/bright_foreground_material_light"/>
+ <item android:color="@android:color/bright_foreground_material_light"/>
+</selector>
diff --git a/core/res/res/color/primary_text_disable_only_quantum_dark.xml b/core/res/res/color/primary_text_disable_only_quantum_dark.xml
deleted file mode 100644
index 60a91f2..0000000
--- a/core/res/res/color/primary_text_disable_only_quantum_dark.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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_enabled="false" android:alpha="0.5" android:color="@android:color/bright_foreground_quantum_dark"/>
- <item android:color="@android:color/bright_foreground_quantum_dark"/>
-</selector>
diff --git a/core/res/res/color/primary_text_disable_only_quantum_light.xml b/core/res/res/color/primary_text_disable_only_quantum_light.xml
deleted file mode 100644
index ced9051..0000000
--- a/core/res/res/color/primary_text_disable_only_quantum_light.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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_enabled="false" android:alpha="0.5" android:color="@android:color/bright_foreground_quantum_light"/>
- <item android:color="@android:color/bright_foreground_quantum_light"/>
-</selector>
diff --git a/core/res/res/color/primary_text_material_dark.xml b/core/res/res/color/primary_text_material_dark.xml
new file mode 100644
index 0000000..caef9d0
--- /dev/null
+++ b/core/res/res/color/primary_text_material_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:alpha="0.5" android:color="@color/primary_text_default_material_dark"/>
+ <item android:color="@color/primary_text_default_material_dark"/>
+</selector>
diff --git a/core/res/res/color/primary_text_material_light.xml b/core/res/res/color/primary_text_material_light.xml
new file mode 100644
index 0000000..81a593b
--- /dev/null
+++ b/core/res/res/color/primary_text_material_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:alpha="0.5" android:color="@color/primary_text_default_material_light"/>
+ <item android:color="@color/primary_text_default_material_light"/>
+</selector>
diff --git a/core/res/res/color/primary_text_quantum_dark.xml b/core/res/res/color/primary_text_quantum_dark.xml
deleted file mode 100644
index 1fcd0e3..0000000
--- a/core/res/res/color/primary_text_quantum_dark.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@color/primary_text_default_quantum_dark"/>
- <item android:color="@color/primary_text_default_quantum_dark"/>
-</selector>
diff --git a/core/res/res/color/primary_text_quantum_light.xml b/core/res/res/color/primary_text_quantum_light.xml
deleted file mode 100644
index 1ec1634..0000000
--- a/core/res/res/color/primary_text_quantum_light.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:alpha="0.5" android:color="@color/primary_text_default_quantum_light"/>
- <item android:color="@color/primary_text_default_quantum_light"/>
-</selector>
diff --git a/core/res/res/color/search_url_text_quantum_dark.xml b/core/res/res/color/search_url_text_material_dark.xml
similarity index 100%
rename from core/res/res/color/search_url_text_quantum_dark.xml
rename to core/res/res/color/search_url_text_material_dark.xml
diff --git a/core/res/res/color/search_url_text_quantum_light.xml b/core/res/res/color/search_url_text_material_light.xml
similarity index 100%
rename from core/res/res/color/search_url_text_quantum_light.xml
rename to core/res/res/color/search_url_text_material_light.xml
diff --git a/core/res/res/drawable-hdpi/ab_share_pack_qntm_alpha.9.png b/core/res/res/drawable-hdpi/ab_share_pack_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ab_share_pack_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/ab_share_pack_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ab_solid_shadow_qntm_alpha.9.png b/core/res/res/drawable-hdpi/ab_solid_shadow_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ab_solid_shadow_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/ab_solid_shadow_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_cab_done_qntm_alpha.9.png b/core/res/res/drawable-hdpi/btn_cab_done_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_cab_done_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/btn_cab_done_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_000.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_001.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_002.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_003.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_004.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_005.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_006.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_007.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_008.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_009.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_010.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_011.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_012.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_013.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_014.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_off_qntm_015.png
rename to core/res/res/drawable-hdpi/btn_check_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_000.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_001.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_002.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_003.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_004.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_005.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_006.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_007.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_008.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_009.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_010.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_011.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_012.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_013.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_014.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_check_to_on_qntm_015.png
rename to core/res/res/drawable-hdpi/btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_default.png b/core/res/res/drawable-hdpi/btn_code_lock_default.png
deleted file mode 100644
index 4469ce0..0000000
--- a/core/res/res/drawable-hdpi/btn_code_lock_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_default_holo.png b/core/res/res/drawable-hdpi/btn_code_lock_default_holo.png
deleted file mode 100644
index 449d427..0000000
--- a/core/res/res/drawable-hdpi/btn_code_lock_default_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_default_mtrl_alpha.png b/core/res/res/drawable-hdpi/btn_code_lock_default_mtrl_alpha.png
new file mode 100644
index 0000000..7cc3c11
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_code_lock_default_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_touched.png b/core/res/res/drawable-hdpi/btn_code_lock_touched.png
deleted file mode 100644
index 0410dd3..0000000
--- a/core/res/res/drawable-hdpi/btn_code_lock_touched.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_touched_holo.png b/core/res/res/drawable-hdpi/btn_code_lock_touched_holo.png
deleted file mode 100644
index 66cb1ec..0000000
--- a/core/res/res/drawable-hdpi/btn_code_lock_touched_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_touched_mtrl_alpha.png b/core/res/res/drawable-hdpi/btn_code_lock_touched_mtrl_alpha.png
new file mode 100644
index 0000000..70397d21
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_code_lock_touched_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_qntm_alpha.9.png b/core/res/res/drawable-hdpi/btn_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/btn_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_000.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_001.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_002.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_003.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_004.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_005.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_006.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_007.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_008.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_009.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_010.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_011.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_012.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_013.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_014.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_off_qntm_015.png
rename to core/res/res/drawable-hdpi/btn_radio_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_000.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_001.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_002.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_003.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_004.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_005.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_006.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_007.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_008.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_009.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_010.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_011.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_012.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_013.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_014.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_radio_to_on_qntm_015.png
rename to core/res/res/drawable-hdpi/btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_rating_star_off_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_rating_star_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_rating_star_off_qntm_alpha.png
rename to core/res/res/drawable-hdpi/btn_rating_star_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_rating_star_on_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_rating_star_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_rating_star_on_qntm_alpha.png
rename to core/res/res/drawable-hdpi/btn_rating_star_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_star_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_star_qntm_alpha.png
rename to core/res/res/drawable-hdpi/btn_star_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_000.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_001.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_002.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_003.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_004.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_005.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_006.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_007.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_008.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_009.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_010.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_011.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_012.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_013.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_off_qntm_014.png
rename to core/res/res/drawable-hdpi/btn_switch_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_000.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_001.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_002.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_003.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_004.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_005.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_006.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_007.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_008.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_009.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_010.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_011.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_012.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_013.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_switch_to_on_qntm_014.png
rename to core/res/res/drawable-hdpi/btn_switch_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-hdpi/btn_toggle_indicator_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_toggle_indicator_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/btn_toggle_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-hdpi/btn_toggle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/btn_toggle_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/btn_toggle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_background_mtrl_mult.9.png b/core/res/res/drawable-hdpi/dialog_background_mtrl_mult.9.png
new file mode 100644
index 0000000..0c3c3b4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_background_mtrl_mult.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/expander_close_qntm_alpha.9.png b/core/res/res/drawable-hdpi/expander_close_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/expander_close_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/expander_close_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/expander_open_qntm_alpha.9.png b/core/res/res/drawable-hdpi/expander_open_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/expander_open_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/expander_open_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_thumb_qntm_alpha.png b/core/res/res/drawable-hdpi/fastscroll_thumb_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/fastscroll_thumb_qntm_alpha.png
rename to core/res/res/drawable-hdpi/fastscroll_thumb_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/fastscroll_track_qntm_alpha.9.png b/core/res/res/drawable-hdpi/fastscroll_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/fastscroll_track_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/fastscroll_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_ab_back_qntm_am_alpha.png b/core/res/res/drawable-hdpi/ic_ab_back_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_ab_back_qntm_am_alpha.png
rename to core/res/res/drawable-hdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_afw_icon.png b/core/res/res/drawable-hdpi/ic_afw_icon.png
new file mode 100644
index 0000000..fc986fe
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_afw_icon.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_cab_done_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_cab_done_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_cab_done_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_cab_done_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_clear_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_clear_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_clear_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_commit_search_api_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_commit_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_commit_search_api_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_commit_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_corp_badge.png b/core/res/res/drawable-hdpi/ic_corp_badge.png
deleted file mode 100644
index f647375..0000000
--- a/core/res/res/drawable-hdpi/ic_corp_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_alert_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_dialog_alert_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_dialog_alert_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_dialog_alert_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_find_next_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_find_next_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_find_next_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_find_next_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_find_previous_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_find_previous_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_find_previous_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_find_previous_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_go_search_api_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_go_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_go_search_api_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_go_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_disabled_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_disabled_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_media_route_disabled_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_media_route_disabled_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_media_route_off_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_media_route_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_0_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_on_0_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_media_route_on_0_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_media_route_on_0_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_1_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_on_1_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_media_route_on_1_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_media_route_on_1_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_2_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_on_2_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_media_route_on_2_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_media_route_on_2_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_media_route_on_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_media_route_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_copy_qntm_am_alpha.png b/core/res/res/drawable-hdpi/ic_menu_copy_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_menu_copy_qntm_am_alpha.png
rename to core/res/res/drawable-hdpi/ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cut_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_menu_cut_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_menu_cut_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_find_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_menu_find_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_menu_find_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_menu_find_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_menu_moreoverflow_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_paste_qntm_am_alpha.png b/core/res/res/drawable-hdpi/ic_menu_paste_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_menu_paste_qntm_am_alpha.png
rename to core/res/res/drawable-hdpi/ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_search_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_menu_search_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_menu_search_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_selectall_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_menu_selectall_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_menu_selectall_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_share_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_menu_share_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_menu_share_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_search_api_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_search_api_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_voice_search_api_qntm_alpha.png b/core/res/res/drawable-hdpi/ic_voice_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/ic_voice_search_api_qntm_alpha.png
rename to core/res/res/drawable-hdpi/ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index 698c3ec..0000000
--- a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
new file mode 100644
index 0000000..b9b400f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
deleted file mode 100644
index c45b956..0000000
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default_holo.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default_holo.png
deleted file mode 100644
index 7fe402a..0000000
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default_mtrl_alpha.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default_mtrl_alpha.png
new file mode 100644
index 0000000..b1601f4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
deleted file mode 100644
index b9fd0a4..0000000
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green_holo.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green_holo.png
deleted file mode 100644
index 4052eed..0000000
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_mtrl_alpha.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_mtrl_alpha.png
new file mode 100644
index 0000000..a038a13
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
deleted file mode 100644
index 94e947d..0000000
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red_holo.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red_holo.png
deleted file mode 100644
index 738d0fe..0000000
--- a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_divider_qntm_alpha.9.png b/core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/list_divider_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_divider_qntm_alpha.9.png b/core/res/res/drawable-hdpi/list_section_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/list_section_divider_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/list_section_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_accessibility_features.png b/core/res/res/drawable-hdpi/perm_group_accessibility_features.png
old mode 100755
new mode 100644
index 849c19c..c2b5960
--- a/core/res/res/drawable-hdpi/perm_group_accessibility_features.png
+++ b/core/res/res/drawable-hdpi/perm_group_accessibility_features.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_accounts.png b/core/res/res/drawable-hdpi/perm_group_accounts.png
index db59ab0..5ac28e7 100644
--- a/core/res/res/drawable-hdpi/perm_group_accounts.png
+++ b/core/res/res/drawable-hdpi/perm_group_accounts.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_affects_battery.png b/core/res/res/drawable-hdpi/perm_group_affects_battery.png
index 8ca8154..945b4686 100644
--- a/core/res/res/drawable-hdpi/perm_group_affects_battery.png
+++ b/core/res/res/drawable-hdpi/perm_group_affects_battery.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_app_info.png b/core/res/res/drawable-hdpi/perm_group_app_info.png
index b03e2f3..754529e 100644
--- a/core/res/res/drawable-hdpi/perm_group_app_info.png
+++ b/core/res/res/drawable-hdpi/perm_group_app_info.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_audio_settings.png b/core/res/res/drawable-hdpi/perm_group_audio_settings.png
index 4e652a8..7e5808a 100644
--- a/core/res/res/drawable-hdpi/perm_group_audio_settings.png
+++ b/core/res/res/drawable-hdpi/perm_group_audio_settings.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_bluetooth.png b/core/res/res/drawable-hdpi/perm_group_bluetooth.png
index 0f28454..dac7071 100644
--- a/core/res/res/drawable-hdpi/perm_group_bluetooth.png
+++ b/core/res/res/drawable-hdpi/perm_group_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_bookmarks.png b/core/res/res/drawable-hdpi/perm_group_bookmarks.png
index 06f63445..0cb0c1d 100644
--- a/core/res/res/drawable-hdpi/perm_group_bookmarks.png
+++ b/core/res/res/drawable-hdpi/perm_group_bookmarks.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_calendar.png b/core/res/res/drawable-hdpi/perm_group_calendar.png
index c0a4dfd..1ef032f 100644
--- a/core/res/res/drawable-hdpi/perm_group_calendar.png
+++ b/core/res/res/drawable-hdpi/perm_group_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_camera.png b/core/res/res/drawable-hdpi/perm_group_camera.png
index cbc07b0..fb5a2f5 100644
--- a/core/res/res/drawable-hdpi/perm_group_camera.png
+++ b/core/res/res/drawable-hdpi/perm_group_camera.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_device_alarms.png b/core/res/res/drawable-hdpi/perm_group_device_alarms.png
index d44b9de..f38c5fe 100644
--- a/core/res/res/drawable-hdpi/perm_group_device_alarms.png
+++ b/core/res/res/drawable-hdpi/perm_group_device_alarms.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_display.png b/core/res/res/drawable-hdpi/perm_group_display.png
index e8afece..c10c5d8 100644
--- a/core/res/res/drawable-hdpi/perm_group_display.png
+++ b/core/res/res/drawable-hdpi/perm_group_display.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_location.png b/core/res/res/drawable-hdpi/perm_group_location.png
index dc2f8ef..6989b16 100644
--- a/core/res/res/drawable-hdpi/perm_group_location.png
+++ b/core/res/res/drawable-hdpi/perm_group_location.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_messages.png b/core/res/res/drawable-hdpi/perm_group_messages.png
index 680c178..21929b6 100644
--- a/core/res/res/drawable-hdpi/perm_group_messages.png
+++ b/core/res/res/drawable-hdpi/perm_group_messages.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_microphone.png b/core/res/res/drawable-hdpi/perm_group_microphone.png
index a73a945..9f8a681 100644
--- a/core/res/res/drawable-hdpi/perm_group_microphone.png
+++ b/core/res/res/drawable-hdpi/perm_group_microphone.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_network.png b/core/res/res/drawable-hdpi/perm_group_network.png
index c750e2a..0ecb7b2 100644
--- a/core/res/res/drawable-hdpi/perm_group_network.png
+++ b/core/res/res/drawable-hdpi/perm_group_network.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_personal_info.png b/core/res/res/drawable-hdpi/perm_group_personal_info.png
index 130e7ad..e1b8a5b 100644
--- a/core/res/res/drawable-hdpi/perm_group_personal_info.png
+++ b/core/res/res/drawable-hdpi/perm_group_personal_info.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_phone_calls.png b/core/res/res/drawable-hdpi/perm_group_phone_calls.png
index 577855b..8fa802e 100644
--- a/core/res/res/drawable-hdpi/perm_group_phone_calls.png
+++ b/core/res/res/drawable-hdpi/perm_group_phone_calls.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_screenlock.png b/core/res/res/drawable-hdpi/perm_group_screenlock.png
index 9c5143d..b5a19ee 100644
--- a/core/res/res/drawable-hdpi/perm_group_screenlock.png
+++ b/core/res/res/drawable-hdpi/perm_group_screenlock.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_shortrange_network.png b/core/res/res/drawable-hdpi/perm_group_shortrange_network.png
index 554a4e4..99ca933 100644
--- a/core/res/res/drawable-hdpi/perm_group_shortrange_network.png
+++ b/core/res/res/drawable-hdpi/perm_group_shortrange_network.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_social_info.png b/core/res/res/drawable-hdpi/perm_group_social_info.png
index 134990b..dcf67e4 100644
--- a/core/res/res/drawable-hdpi/perm_group_social_info.png
+++ b/core/res/res/drawable-hdpi/perm_group_social_info.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_status_bar.png b/core/res/res/drawable-hdpi/perm_group_status_bar.png
index bda963b..e3f6ed9 100644
--- a/core/res/res/drawable-hdpi/perm_group_status_bar.png
+++ b/core/res/res/drawable-hdpi/perm_group_status_bar.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_storage.png b/core/res/res/drawable-hdpi/perm_group_storage.png
index e6b3965..598e1cc 100644
--- a/core/res/res/drawable-hdpi/perm_group_storage.png
+++ b/core/res/res/drawable-hdpi/perm_group_storage.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_sync_settings.png b/core/res/res/drawable-hdpi/perm_group_sync_settings.png
index be70866..1ca6b17 100644
--- a/core/res/res/drawable-hdpi/perm_group_sync_settings.png
+++ b/core/res/res/drawable-hdpi/perm_group_sync_settings.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_system_clock.png b/core/res/res/drawable-hdpi/perm_group_system_clock.png
index 75794c3..3dd4682 100644
--- a/core/res/res/drawable-hdpi/perm_group_system_clock.png
+++ b/core/res/res/drawable-hdpi/perm_group_system_clock.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_system_tools.png b/core/res/res/drawable-hdpi/perm_group_system_tools.png
index 3fd4385..ae1ba2a 100644
--- a/core/res/res/drawable-hdpi/perm_group_system_tools.png
+++ b/core/res/res/drawable-hdpi/perm_group_system_tools.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_user_dictionary.png b/core/res/res/drawable-hdpi/perm_group_user_dictionary.png
index 98a0894..62fbcdc 100644
--- a/core/res/res/drawable-hdpi/perm_group_user_dictionary.png
+++ b/core/res/res/drawable-hdpi/perm_group_user_dictionary.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_user_dictionary_write.png b/core/res/res/drawable-hdpi/perm_group_user_dictionary_write.png
index 784ea0f..c62dd4c 100644
--- a/core/res/res/drawable-hdpi/perm_group_user_dictionary_write.png
+++ b/core/res/res/drawable-hdpi/perm_group_user_dictionary_write.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_voicemail.png b/core/res/res/drawable-hdpi/perm_group_voicemail.png
index b08b153..3b245d6 100644
--- a/core/res/res/drawable-hdpi/perm_group_voicemail.png
+++ b/core/res/res/drawable-hdpi/perm_group_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/perm_group_wallpaper.png b/core/res/res/drawable-hdpi/perm_group_wallpaper.png
index cf073a4..e40c4f0 100644
--- a/core/res/res/drawable-hdpi/perm_group_wallpaper.png
+++ b/core/res/res/drawable-hdpi/perm_group_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_background_qntm_mult.9.png b/core/res/res/drawable-hdpi/popup_background_mtrl_mult.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/popup_background_qntm_mult.9.png
rename to core/res/res/drawable-hdpi/popup_background_mtrl_mult.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progress_qntm_alpha.9.png b/core/res/res/drawable-hdpi/progress_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/progress_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/progress_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progress_primary_qntm_alpha.9.png b/core/res/res/drawable-hdpi/progress_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/progress_primary_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/progress_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrollbar_handle_qntm_alpha.9.png b/core/res/res/drawable-hdpi/scrollbar_handle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrollbar_handle_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/scrollbar_handle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_000.png
rename to core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_001.png
rename to core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_002.png
rename to core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_003.png
rename to core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_004.png
rename to core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_from_pressed_qntm_005.png
rename to core/res/res/drawable-hdpi/scrubber_control_from_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
rename to core/res/res/drawable-hdpi/scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_off_pressed_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_off_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_off_pressed_qntm_alpha.png
rename to core/res/res/drawable-hdpi/scrubber_control_off_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
rename to core/res/res/drawable-hdpi/scrubber_control_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_on_pressed_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_on_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_on_pressed_qntm_alpha.png
rename to core/res/res/drawable-hdpi/scrubber_control_on_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_000.png
rename to core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_001.png
rename to core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_002.png
rename to core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_003.png
rename to core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_004.png
rename to core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_control_to_pressed_qntm_005.png
rename to core/res/res/drawable-hdpi/scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_primary_qntm_alpha.9.png b/core/res/res/drawable-hdpi/scrubber_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_primary_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_track_qntm_alpha.9.png b/core/res/res/drawable-hdpi/scrubber_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/scrubber_track_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/scrubber_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png b/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
deleted file mode 100644
index 49841ea..0000000
--- a/core/res/res/drawable-hdpi/spinner_20_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_20_outer_holo.png b/core/res/res/drawable-hdpi/spinner_20_outer_holo.png
deleted file mode 100644
index 69f0070..0000000
--- a/core/res/res/drawable-hdpi/spinner_20_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_qntm_am_alpha.9.png b/core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/spinner_qntm_am_alpha.9.png
rename to core/res/res/drawable-hdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/switch_track_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_indicator_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/tab_indicator_mtrl_alpha.9.png
new file mode 100644
index 0000000..21b2135
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_indicator_normal_qntm_alpha.9.png b/core/res/res/drawable-hdpi/tab_indicator_normal_qntm_alpha.9.png
deleted file mode 100644
index 233d409..0000000
--- a/core/res/res/drawable-hdpi/tab_indicator_normal_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_indicator_selected_qntm_alpha.9.png b/core/res/res/drawable-hdpi/tab_indicator_selected_qntm_alpha.9.png
deleted file mode 100644
index 68b1dd7..0000000
--- a/core/res/res/drawable-hdpi/tab_indicator_selected_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_cursor_qntm_alpha.9.png b/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/text_cursor_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_left_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/text_select_handle_left_qntm_alpha.png
rename to core/res/res/drawable-hdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_middle_qntm_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_middle_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/text_select_handle_middle_qntm_alpha.png
rename to core/res/res/drawable-hdpi/text_select_handle_middle_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-hdpi/text_select_handle_right_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-hdpi/text_select_handle_right_qntm_alpha.png
rename to core/res/res/drawable-hdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_activated_qntm_alpha.9.png b/core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/textfield_activated_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default_qntm_alpha.9.png b/core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/textfield_default_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_activated_qntm_alpha.9.png b/core/res/res/drawable-hdpi/textfield_search_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/textfield_search_activated_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/textfield_search_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_default_qntm_alpha.9.png b/core/res/res/drawable-hdpi/textfield_search_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-hdpi/textfield_search_default_qntm_alpha.9.png
rename to core/res/res/drawable-hdpi/textfield_search_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/work_icon.png b/core/res/res/drawable-hdpi/work_icon.png
index e90866b..be8a36e 100644
--- a/core/res/res/drawable-hdpi/work_icon.png
+++ b/core/res/res/drawable-hdpi/work_icon.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ab_solid_shadow_qntm.9.png b/core/res/res/drawable-ldpi/ab_solid_shadow_mtrl.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ab_solid_shadow_qntm.9.png
rename to core/res/res/drawable-ldpi/ab_solid_shadow_mtrl.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ab_transparent_qntm_alpha.9.png b/core/res/res/drawable-ldpi/ab_transparent_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ab_transparent_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/ab_transparent_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_check_off_qntm_alpha.png b/core/res/res/drawable-ldpi/btn_check_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/btn_check_off_qntm_alpha.png
rename to core/res/res/drawable-ldpi/btn_check_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_check_on_qntm_alpha.png b/core/res/res/drawable-ldpi/btn_check_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/btn_check_on_qntm_alpha.png
rename to core/res/res/drawable-ldpi/btn_check_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_code_lock_default.png b/core/res/res/drawable-ldpi/btn_code_lock_default.png
deleted file mode 100644
index 149da9b..0000000
--- a/core/res/res/drawable-ldpi/btn_code_lock_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_code_lock_touched.png b/core/res/res/drawable-ldpi/btn_code_lock_touched.png
deleted file mode 100644
index ad9a313..0000000
--- a/core/res/res/drawable-ldpi/btn_code_lock_touched.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_qntm_alpha.9.png b/core/res/res/drawable-ldpi/btn_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/btn_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/btn_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_radio_off_qntm_alpha.png b/core/res/res/drawable-ldpi/btn_radio_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/btn_radio_off_qntm_alpha.png
rename to core/res/res/drawable-ldpi/btn_radio_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_radio_off_pressed_qntm_alpha.png b/core/res/res/drawable-ldpi/btn_radio_off_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/btn_radio_off_pressed_qntm_alpha.png
rename to core/res/res/drawable-ldpi/btn_radio_off_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_radio_on_qntm_alpha.png b/core/res/res/drawable-ldpi/btn_radio_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/btn_radio_on_qntm_alpha.png
rename to core/res/res/drawable-ldpi/btn_radio_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-ldpi/btn_radio_on_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/btn_radio_on_pressed_qntm_alpha.png
rename to core/res/res/drawable-ldpi/btn_radio_on_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_star_qntm_alpha.png b/core/res/res/drawable-ldpi/btn_star_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/btn_star_qntm_alpha.png
rename to core/res/res/drawable-ldpi/btn_star_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/expander_close_qntm_alpha.9.png b/core/res/res/drawable-ldpi/expander_close_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/expander_close_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/expander_close_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/expander_open_qntm_alpha.9.png b/core/res/res/drawable-ldpi/expander_open_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/expander_open_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/expander_open_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/fastscroll_thumb_qntm_alpha.png b/core/res/res/drawable-ldpi/fastscroll_thumb_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/fastscroll_thumb_qntm_alpha.png
rename to core/res/res/drawable-ldpi/fastscroll_thumb_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/fastscroll_track_qntm_alpha.9.png b/core/res/res/drawable-ldpi/fastscroll_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/fastscroll_track_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/fastscroll_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_ab_back_qntm_am_alpha.png b/core/res/res/drawable-ldpi/ic_ab_back_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_ab_back_qntm_am_alpha.png
rename to core/res/res/drawable-ldpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_cab_done_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_cab_done_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_cab_done_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_cab_done_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_dialog_alert_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_dialog_alert_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_dialog_alert_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_dialog_alert_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_find_next_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_find_next_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_find_next_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_find_next_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_find_previous_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_find_previous_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_find_previous_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_find_previous_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_menu_copy_qntm_am_alpha.png b/core/res/res/drawable-ldpi/ic_menu_copy_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_menu_copy_qntm_am_alpha.png
rename to core/res/res/drawable-ldpi/ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_menu_cut_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_menu_cut_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_menu_cut_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_menu_find_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_menu_find_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_menu_find_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_menu_find_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_menu_moreoverflow_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_menu_moreoverflow_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_menu_moreoverflow_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_menu_paste_qntm_am_alpha.png b/core/res/res/drawable-ldpi/ic_menu_paste_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_menu_paste_qntm_am_alpha.png
rename to core/res/res/drawable-ldpi/ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_menu_search_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_menu_search_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_menu_search_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_menu_selectall_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_menu_selectall_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_menu_selectall_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/ic_menu_share_qntm_alpha.png b/core/res/res/drawable-ldpi/ic_menu_share_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/ic_menu_share_qntm_alpha.png
rename to core/res/res/drawable-ldpi/ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-ldpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index ac8e42a..0000000
--- a/core/res/res/drawable-ldpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-ldpi/indicator_code_lock_point_area_default.png
deleted file mode 100644
index 5b77b9f..0000000
--- a/core/res/res/drawable-ldpi/indicator_code_lock_point_area_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-ldpi/indicator_code_lock_point_area_green.png
deleted file mode 100644
index c7c0b9a..0000000
--- a/core/res/res/drawable-ldpi/indicator_code_lock_point_area_green.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-ldpi/indicator_code_lock_point_area_red.png
deleted file mode 100644
index ac02dc4..0000000
--- a/core/res/res/drawable-ldpi/indicator_code_lock_point_area_red.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/list_divider_qntm_alpha.9.png b/core/res/res/drawable-ldpi/list_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/list_divider_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/list_section_divider_qntm_alpha.9.png b/core/res/res/drawable-ldpi/list_section_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/list_section_divider_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/list_section_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/progress_qntm_alpha.9.png b/core/res/res/drawable-ldpi/progress_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/progress_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/progress_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/progress_primary_qntm_alpha.9.png b/core/res/res/drawable-ldpi/progress_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/progress_primary_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/progress_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/scrollbar_handle_qntm_alpha.9.png b/core/res/res/drawable-ldpi/scrollbar_handle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/scrollbar_handle_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/scrollbar_handle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-ldpi/scrubber_control_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/scrubber_control_off_qntm_alpha.png
rename to core/res/res/drawable-ldpi/scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/scrubber_control_off_pressed_qntm_alpha.png b/core/res/res/drawable-ldpi/scrubber_control_off_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/scrubber_control_off_pressed_qntm_alpha.png
rename to core/res/res/drawable-ldpi/scrubber_control_off_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-ldpi/scrubber_control_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-ldpi/scrubber_control_on_qntm_alpha.png
rename to core/res/res/drawable-ldpi/scrubber_control_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-ldpi/scrubber_control_on_pressed_mtrl_alpha.png
similarity index 100%
copy from core/res/res/drawable-ldpi/btn_radio_on_pressed_qntm_alpha.png
copy to core/res/res/drawable-ldpi/scrubber_control_on_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/scrubber_control_on_pressed_qntm_alpha.png b/core/res/res/drawable-ldpi/scrubber_control_on_pressed_qntm_alpha.png
deleted file mode 100644
index 458abaf..0000000
--- a/core/res/res/drawable-ldpi/scrubber_control_on_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/scrubber_primary_qntm_alpha.9.png b/core/res/res/drawable-ldpi/scrubber_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/scrubber_primary_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/spinner_qntm_am_alpha.9.png b/core/res/res/drawable-ldpi/spinner_mtrl_am_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/spinner_qntm_am_alpha.9.png
rename to core/res/res/drawable-ldpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-ldpi/switch_off_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/switch_off_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/switch_off_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-ldpi/switch_on_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/switch_on_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/switch_on_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/progress_qntm_alpha.9.png b/core/res/res/drawable-ldpi/switch_track_mtrl_alpha.9.png
similarity index 100%
copy from core/res/res/drawable-ldpi/progress_qntm_alpha.9.png
copy to core/res/res/drawable-ldpi/switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-ldpi/switch_track_qntm_alpha.9.png
deleted file mode 100644
index a58128f..0000000
--- a/core/res/res/drawable-ldpi/switch_track_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/tab_indicator_normal_qntm_alpha.9.png b/core/res/res/drawable-ldpi/tab_indicator_normal_qntm_alpha.9.png
deleted file mode 100644
index 2244362..0000000
--- a/core/res/res/drawable-ldpi/tab_indicator_normal_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/tab_indicator_selected_qntm_alpha.9.png b/core/res/res/drawable-ldpi/tab_indicator_selected_qntm_alpha.9.png
deleted file mode 100644
index 22ea80e..0000000
--- a/core/res/res/drawable-ldpi/tab_indicator_selected_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/text_cursor_qntm_alpha.9.png b/core/res/res/drawable-ldpi/text_cursor_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/text_cursor_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/textfield_activated_qntm_alpha.9.png b/core/res/res/drawable-ldpi/textfield_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/textfield_activated_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/textfield_default_qntm_alpha.9.png b/core/res/res/drawable-ldpi/textfield_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-ldpi/textfield_default_qntm_alpha.9.png
rename to core/res/res/drawable-ldpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_share_pack_qntm_alpha.9.png b/core/res/res/drawable-mdpi/ab_share_pack_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ab_share_pack_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/ab_share_pack_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ab_solid_shadow_qntm_alpha.9.png b/core/res/res/drawable-mdpi/ab_solid_shadow_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ab_solid_shadow_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/ab_solid_shadow_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_cab_done_qntm_alpha.9.png b/core/res/res/drawable-mdpi/btn_cab_done_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_cab_done_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/btn_cab_done_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_000.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_001.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_002.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_003.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_004.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_005.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_006.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_007.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_008.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_009.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_010.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_011.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_012.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_013.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_014.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_off_qntm_015.png
rename to core/res/res/drawable-mdpi/btn_check_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_000.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_001.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_002.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_003.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_004.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_005.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_006.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_007.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_008.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_009.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_010.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_011.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_012.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_013.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_014.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_check_to_on_qntm_015.png
rename to core/res/res/drawable-mdpi/btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_default.png b/core/res/res/drawable-mdpi/btn_code_lock_default.png
deleted file mode 100644
index 206f9b3..0000000
--- a/core/res/res/drawable-mdpi/btn_code_lock_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_default_holo.png b/core/res/res/drawable-mdpi/btn_code_lock_default_holo.png
deleted file mode 100644
index 4c4adf2..0000000
--- a/core/res/res/drawable-mdpi/btn_code_lock_default_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_default_mtrl_alpha.png b/core/res/res/drawable-mdpi/btn_code_lock_default_mtrl_alpha.png
new file mode 100644
index 0000000..14d0b32
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_code_lock_default_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
deleted file mode 100644
index fe5c1af..0000000
--- a/core/res/res/drawable-mdpi/btn_code_lock_touched.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_touched_holo.png b/core/res/res/drawable-mdpi/btn_code_lock_touched_holo.png
deleted file mode 100644
index ef701ed..0000000
--- a/core/res/res/drawable-mdpi/btn_code_lock_touched_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_touched_mtrl_alpha.png b/core/res/res/drawable-mdpi/btn_code_lock_touched_mtrl_alpha.png
new file mode 100644
index 0000000..9cfbdf9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_code_lock_touched_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_qntm_alpha.9.png b/core/res/res/drawable-mdpi/btn_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/btn_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_on_qntm_alpha.png
rename to core/res/res/drawable-mdpi/btn_radio_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_on_pressed_qntm_alpha.png
rename to core/res/res/drawable-mdpi/btn_radio_on_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_000.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_001.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_002.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_003.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_004.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_005.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_006.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_007.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_008.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_009.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_010.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_011.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_012.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_013.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_014.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_off_qntm_015.png
rename to core/res/res/drawable-mdpi/btn_radio_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_000.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_001.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_002.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_003.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_004.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_005.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_006.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_007.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_008.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_009.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_010.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_011.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_012.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_013.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_014.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_radio_to_on_qntm_015.png
rename to core/res/res/drawable-mdpi/btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_rating_star_off_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_rating_star_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_rating_star_off_qntm_alpha.png
rename to core/res/res/drawable-mdpi/btn_rating_star_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_rating_star_on_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_rating_star_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_rating_star_on_qntm_alpha.png
rename to core/res/res/drawable-mdpi/btn_rating_star_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_star_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_star_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_star_qntm_alpha.png
rename to core/res/res/drawable-mdpi/btn_star_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_000.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_001.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_002.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_003.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_004.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_005.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_006.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_007.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_008.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_009.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_010.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_011.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_012.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_013.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_off_qntm_014.png
rename to core/res/res/drawable-mdpi/btn_switch_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_000.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_001.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_002.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_003.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_004.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_005.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_006.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_007.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_008.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_009.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_010.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_011.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_012.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_013.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_switch_to_on_qntm_014.png
rename to core/res/res/drawable-mdpi/btn_switch_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-mdpi/btn_toggle_indicator_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_toggle_indicator_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/btn_toggle_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-mdpi/btn_toggle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/btn_toggle_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/btn_toggle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_background_mtrl_mult.9.png b/core/res/res/drawable-mdpi/dialog_background_mtrl_mult.9.png
new file mode 100644
index 0000000..8322ae3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dialog_background_mtrl_mult.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/expander_close_qntm_alpha.9.png b/core/res/res/drawable-mdpi/expander_close_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/expander_close_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/expander_close_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/expander_open_qntm_alpha.9.png b/core/res/res/drawable-mdpi/expander_open_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/expander_open_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/expander_open_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_thumb_qntm_alpha.png b/core/res/res/drawable-mdpi/fastscroll_thumb_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/fastscroll_thumb_qntm_alpha.png
rename to core/res/res/drawable-mdpi/fastscroll_thumb_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/fastscroll_track_qntm_alpha.9.png b/core/res/res/drawable-mdpi/fastscroll_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/fastscroll_track_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/fastscroll_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_ab_back_qntm_am_alpha.png b/core/res/res/drawable-mdpi/ic_ab_back_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_ab_back_qntm_am_alpha.png
rename to core/res/res/drawable-mdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_afw_icon.png b/core/res/res/drawable-mdpi/ic_afw_icon.png
new file mode 100644
index 0000000..deb7966
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_afw_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_cab_done_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_cab_done_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_cab_done_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_cab_done_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_clear_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_clear_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_clear_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_commit_search_api_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_commit_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_commit_search_api_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_commit_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_dialog_alert_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_dialog_alert_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_dialog_alert_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_dialog_alert_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_find_next_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_find_next_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_find_next_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_find_next_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_find_previous_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_find_previous_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_find_previous_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_find_previous_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_go_search_api_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_go_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_go_search_api_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_go_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_disabled_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_disabled_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_media_route_disabled_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_media_route_disabled_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_media_route_off_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_media_route_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_0_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_on_0_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_media_route_on_0_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_media_route_on_0_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_1_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_on_1_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_media_route_on_1_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_media_route_on_1_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_2_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_on_2_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_media_route_on_2_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_media_route_on_2_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_media_route_on_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_media_route_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_copy_qntm_am_alpha.png b/core/res/res/drawable-mdpi/ic_menu_copy_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_menu_copy_qntm_am_alpha.png
rename to core/res/res/drawable-mdpi/ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_cut_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_menu_cut_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_menu_cut_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_find_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_menu_find_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_menu_find_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_menu_find_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_menu_moreoverflow_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_paste_qntm_am_alpha.png b/core/res/res/drawable-mdpi/ic_menu_paste_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_menu_paste_qntm_am_alpha.png
rename to core/res/res/drawable-mdpi/ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_search_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_menu_search_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_menu_search_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_selectall_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_menu_selectall_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_menu_selectall_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_menu_share_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_menu_share_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_menu_share_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_search_api_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_search_api_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_voice_search_api_qntm_alpha.png b/core/res/res/drawable-mdpi/ic_voice_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/ic_voice_search_api_qntm_alpha.png
rename to core/res/res/drawable-mdpi/ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index 7201e58..0000000
--- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
new file mode 100644
index 0000000..2fb1325
--- /dev/null
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
deleted file mode 100644
index 05c194b..0000000
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default_holo.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default_holo.png
deleted file mode 100644
index 5762e5f..0000000
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default_mtrl_alpha.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default_mtrl_alpha.png
new file mode 100644
index 0000000..07d4afd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
deleted file mode 100644
index 8f24832..0000000
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green_holo.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green_holo.png
deleted file mode 100644
index bfb0967..0000000
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_mtrl_alpha.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_mtrl_alpha.png
new file mode 100644
index 0000000..ea8c2b4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red_holo.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red_holo.png
deleted file mode 100644
index 8c0386f..0000000
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_divider_qntm_alpha.9.png b/core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/list_divider_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_divider_qntm_alpha.9.png b/core/res/res/drawable-mdpi/list_section_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/list_section_divider_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/list_section_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_accessibility_features.png b/core/res/res/drawable-mdpi/perm_group_accessibility_features.png
old mode 100755
new mode 100644
index ba86d3d..57c4167
--- a/core/res/res/drawable-mdpi/perm_group_accessibility_features.png
+++ b/core/res/res/drawable-mdpi/perm_group_accessibility_features.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_accounts.png b/core/res/res/drawable-mdpi/perm_group_accounts.png
index 3dd4043..ce4e683 100644
--- a/core/res/res/drawable-mdpi/perm_group_accounts.png
+++ b/core/res/res/drawable-mdpi/perm_group_accounts.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_affects_battery.png b/core/res/res/drawable-mdpi/perm_group_affects_battery.png
index 7291916..55a0b79 100644
--- a/core/res/res/drawable-mdpi/perm_group_affects_battery.png
+++ b/core/res/res/drawable-mdpi/perm_group_affects_battery.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_app_info.png b/core/res/res/drawable-mdpi/perm_group_app_info.png
index 8ba65bd..8393586 100644
--- a/core/res/res/drawable-mdpi/perm_group_app_info.png
+++ b/core/res/res/drawable-mdpi/perm_group_app_info.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_audio_settings.png b/core/res/res/drawable-mdpi/perm_group_audio_settings.png
index f2f461b..734429f 100644
--- a/core/res/res/drawable-mdpi/perm_group_audio_settings.png
+++ b/core/res/res/drawable-mdpi/perm_group_audio_settings.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_bluetooth.png b/core/res/res/drawable-mdpi/perm_group_bluetooth.png
index 6db6fde..4b79c1a 100644
--- a/core/res/res/drawable-mdpi/perm_group_bluetooth.png
+++ b/core/res/res/drawable-mdpi/perm_group_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_bookmarks.png b/core/res/res/drawable-mdpi/perm_group_bookmarks.png
index f908e14..ca3d453 100644
--- a/core/res/res/drawable-mdpi/perm_group_bookmarks.png
+++ b/core/res/res/drawable-mdpi/perm_group_bookmarks.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_calendar.png b/core/res/res/drawable-mdpi/perm_group_calendar.png
index 5905973..d2ad9d6 100644
--- a/core/res/res/drawable-mdpi/perm_group_calendar.png
+++ b/core/res/res/drawable-mdpi/perm_group_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_camera.png b/core/res/res/drawable-mdpi/perm_group_camera.png
index be1c9e6..1727cf1 100644
--- a/core/res/res/drawable-mdpi/perm_group_camera.png
+++ b/core/res/res/drawable-mdpi/perm_group_camera.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_device_alarms.png b/core/res/res/drawable-mdpi/perm_group_device_alarms.png
index 48d6d6a..48339cd 100644
--- a/core/res/res/drawable-mdpi/perm_group_device_alarms.png
+++ b/core/res/res/drawable-mdpi/perm_group_device_alarms.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_display.png b/core/res/res/drawable-mdpi/perm_group_display.png
index e10609c..9738f15 100644
--- a/core/res/res/drawable-mdpi/perm_group_display.png
+++ b/core/res/res/drawable-mdpi/perm_group_display.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_location.png b/core/res/res/drawable-mdpi/perm_group_location.png
index e79ec25..7ad1e02 100644
--- a/core/res/res/drawable-mdpi/perm_group_location.png
+++ b/core/res/res/drawable-mdpi/perm_group_location.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_messages.png b/core/res/res/drawable-mdpi/perm_group_messages.png
index dfb3ba7..12c5d88 100644
--- a/core/res/res/drawable-mdpi/perm_group_messages.png
+++ b/core/res/res/drawable-mdpi/perm_group_messages.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_microphone.png b/core/res/res/drawable-mdpi/perm_group_microphone.png
index 9bab315..f6015b5 100644
--- a/core/res/res/drawable-mdpi/perm_group_microphone.png
+++ b/core/res/res/drawable-mdpi/perm_group_microphone.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_network.png b/core/res/res/drawable-mdpi/perm_group_network.png
index f2798a7..c575d70 100644
--- a/core/res/res/drawable-mdpi/perm_group_network.png
+++ b/core/res/res/drawable-mdpi/perm_group_network.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_personal_info.png b/core/res/res/drawable-mdpi/perm_group_personal_info.png
index 6233a82..13ec27e 100644
--- a/core/res/res/drawable-mdpi/perm_group_personal_info.png
+++ b/core/res/res/drawable-mdpi/perm_group_personal_info.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_phone_calls.png b/core/res/res/drawable-mdpi/perm_group_phone_calls.png
index ff3ffd5..e373c2c 100644
--- a/core/res/res/drawable-mdpi/perm_group_phone_calls.png
+++ b/core/res/res/drawable-mdpi/perm_group_phone_calls.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_screenlock.png b/core/res/res/drawable-mdpi/perm_group_screenlock.png
index abfe6e4..9d9bb75 100644
--- a/core/res/res/drawable-mdpi/perm_group_screenlock.png
+++ b/core/res/res/drawable-mdpi/perm_group_screenlock.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_shortrange_network.png b/core/res/res/drawable-mdpi/perm_group_shortrange_network.png
index 5d73375..5d35676 100644
--- a/core/res/res/drawable-mdpi/perm_group_shortrange_network.png
+++ b/core/res/res/drawable-mdpi/perm_group_shortrange_network.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_social_info.png b/core/res/res/drawable-mdpi/perm_group_social_info.png
index c862f9e..4ca0767 100644
--- a/core/res/res/drawable-mdpi/perm_group_social_info.png
+++ b/core/res/res/drawable-mdpi/perm_group_social_info.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_status_bar.png b/core/res/res/drawable-mdpi/perm_group_status_bar.png
index 4158fa6..f10536b 100644
--- a/core/res/res/drawable-mdpi/perm_group_status_bar.png
+++ b/core/res/res/drawable-mdpi/perm_group_status_bar.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_storage.png b/core/res/res/drawable-mdpi/perm_group_storage.png
index 3dcfb22..b7a06fb 100644
--- a/core/res/res/drawable-mdpi/perm_group_storage.png
+++ b/core/res/res/drawable-mdpi/perm_group_storage.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_sync_settings.png b/core/res/res/drawable-mdpi/perm_group_sync_settings.png
index 5a0e5ff..f5ef82b 100644
--- a/core/res/res/drawable-mdpi/perm_group_sync_settings.png
+++ b/core/res/res/drawable-mdpi/perm_group_sync_settings.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_system_clock.png b/core/res/res/drawable-mdpi/perm_group_system_clock.png
index e4d5743..3a67642 100644
--- a/core/res/res/drawable-mdpi/perm_group_system_clock.png
+++ b/core/res/res/drawable-mdpi/perm_group_system_clock.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_system_tools.png b/core/res/res/drawable-mdpi/perm_group_system_tools.png
index fc7337d..fd282e6 100644
--- a/core/res/res/drawable-mdpi/perm_group_system_tools.png
+++ b/core/res/res/drawable-mdpi/perm_group_system_tools.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_user_dictionary.png b/core/res/res/drawable-mdpi/perm_group_user_dictionary.png
index 92864ba..a303dc1 100644
--- a/core/res/res/drawable-mdpi/perm_group_user_dictionary.png
+++ b/core/res/res/drawable-mdpi/perm_group_user_dictionary.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_user_dictionary_write.png b/core/res/res/drawable-mdpi/perm_group_user_dictionary_write.png
index 9f48713..2fc4056 100644
--- a/core/res/res/drawable-mdpi/perm_group_user_dictionary_write.png
+++ b/core/res/res/drawable-mdpi/perm_group_user_dictionary_write.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_voicemail.png b/core/res/res/drawable-mdpi/perm_group_voicemail.png
index a34d689..108a725 100644
--- a/core/res/res/drawable-mdpi/perm_group_voicemail.png
+++ b/core/res/res/drawable-mdpi/perm_group_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/perm_group_wallpaper.png b/core/res/res/drawable-mdpi/perm_group_wallpaper.png
index b990e7f..aa06f38 100644
--- a/core/res/res/drawable-mdpi/perm_group_wallpaper.png
+++ b/core/res/res/drawable-mdpi/perm_group_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/popup_background_qntm_mult.9.png b/core/res/res/drawable-mdpi/popup_background_mtrl_mult.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/popup_background_qntm_mult.9.png
rename to core/res/res/drawable-mdpi/popup_background_mtrl_mult.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progress_qntm_alpha.9.png b/core/res/res/drawable-mdpi/progress_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/progress_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/progress_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/progress_primary_qntm_alpha.9.png b/core/res/res/drawable-mdpi/progress_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/progress_primary_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/progress_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrollbar_handle_qntm_alpha.9.png b/core/res/res/drawable-mdpi/scrollbar_handle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrollbar_handle_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/scrollbar_handle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_000.png
rename to core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_001.png
rename to core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_002.png
rename to core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_003.png
rename to core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_004.png
rename to core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_from_pressed_qntm_005.png
rename to core/res/res/drawable-mdpi/scrubber_control_from_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
rename to core/res/res/drawable-mdpi/scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_off_pressed_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_off_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_off_pressed_qntm_alpha.png
rename to core/res/res/drawable-mdpi/scrubber_control_off_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
rename to core/res/res/drawable-mdpi/scrubber_control_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_on_pressed_mtrl_alpha.png
similarity index 100%
copy from core/res/res/drawable-mdpi/btn_radio_on_pressed_qntm_alpha.png
copy to core/res/res/drawable-mdpi/scrubber_control_on_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_on_pressed_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_on_pressed_qntm_alpha.png
deleted file mode 100644
index 3c304bf..0000000
--- a/core/res/res/drawable-mdpi/scrubber_control_on_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_000.png
rename to core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_001.png
rename to core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_002.png
rename to core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_003.png
rename to core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_004.png
rename to core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_control_to_pressed_qntm_005.png
rename to core/res/res/drawable-mdpi/scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_primary_qntm_alpha.9.png b/core/res/res/drawable-mdpi/scrubber_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_primary_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_track_qntm_alpha.9.png b/core/res/res/drawable-mdpi/scrubber_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/scrubber_track_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/scrubber_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png b/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
deleted file mode 100644
index 4569fae..0000000
--- a/core/res/res/drawable-mdpi/spinner_20_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_20_outer_holo.png b/core/res/res/drawable-mdpi/spinner_20_outer_holo.png
deleted file mode 100644
index 9287dd7..0000000
--- a/core/res/res/drawable-mdpi/spinner_20_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_qntm_am_alpha.9.png b/core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/spinner_qntm_am_alpha.9.png
rename to core/res/res/drawable-mdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/switch_track_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_indicator_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/tab_indicator_mtrl_alpha.9.png
new file mode 100644
index 0000000..b69529c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_indicator_normal_qntm_alpha.9.png b/core/res/res/drawable-mdpi/tab_indicator_normal_qntm_alpha.9.png
deleted file mode 100644
index fd668ee..0000000
--- a/core/res/res/drawable-mdpi/tab_indicator_normal_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_indicator_selected_qntm_alpha.9.png b/core/res/res/drawable-mdpi/tab_indicator_selected_qntm_alpha.9.png
deleted file mode 100644
index ace579f..0000000
--- a/core/res/res/drawable-mdpi/tab_indicator_selected_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_qntm_alpha.9.png b/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/text_cursor_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_left_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/text_select_handle_left_qntm_alpha.png
rename to core/res/res/drawable-mdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_middle_qntm_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_middle_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/text_select_handle_middle_qntm_alpha.png
rename to core/res/res/drawable-mdpi/text_select_handle_middle_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-mdpi/text_select_handle_right_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-mdpi/text_select_handle_right_qntm_alpha.png
rename to core/res/res/drawable-mdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_activated_qntm_alpha.9.png b/core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/textfield_activated_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_default_qntm_alpha.9.png b/core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/textfield_default_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_activated_qntm_alpha.9.png b/core/res/res/drawable-mdpi/textfield_search_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/textfield_search_activated_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/textfield_search_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_search_default_qntm_alpha.9.png b/core/res/res/drawable-mdpi/textfield_search_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-mdpi/textfield_search_default_qntm_alpha.9.png
rename to core/res/res/drawable-mdpi/textfield_search_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index cc46f19..0000000
--- a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index cc46f19..0000000
--- a/core/res/res/drawable-nodpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index 668cff7..d1e2df3 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -19,18 +19,21 @@
<viewport android:viewportHeight="25" android:viewportWidth="25" />
<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"
+ android:name="torso"
+ android:pathData="m2,2 l21,0 l0,21 l-21,0 z"
+ android:fill="#FFFFFFFF"
/>
+
<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"
+ android:name="|"
+ android:pathData="m4,4 l8,0 l0,17 l-8,0 z"
+ android:fill="#FF0000FF"
+ />
+
+ <path
+ android:name="_"
+ android:pathData="m5,14 l16,0 l0,6 l-16,0 z"
+ android:fill="#FFFF0000"
/>
</vector>
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index b8ddb77..6b3be4a 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -18,13 +18,26 @@
<viewport android:viewportHeight="25" android:viewportWidth="25" />
-
<path
- android:name="adb"
- android:pathData="m3,3l8,0l0,11l11,0l0,8l-19,0z"
+ android:name="L-card"
+
+ android:pathData="
+ m4,2
+ a2,2,0,0,0,-2,2 l0,17
+ a2,2,0,0,0,2,2 l17,0
+ a2,2,0,0,0,2,-2 l0,-17
+ a2,2,0,0,0,-2,-2
+ z
+
+ M7,2 l3,0 l0,13 l13,0 l0,3 l-16,0
+
+ M15,2 l3,0 l0,5 l5,0 l0,3 l-8,0
+
+ z"
android:fill="#FFFFFFFF"
/>
+
</vector>
diff --git a/core/res/res/drawable-sw600dp-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-sw600dp-mdpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index 2ab4547..0000000
--- a/core/res/res/drawable-sw600dp-mdpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_share_pack_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/ab_share_pack_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ab_share_pack_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/ab_share_pack_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ab_solid_shadow_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/ab_solid_shadow_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ab_solid_shadow_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/ab_solid_shadow_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_cab_done_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/btn_cab_done_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_000.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_001.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_002.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_003.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_004.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_005.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_006.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_007.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_008.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_009.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_010.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_011.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_012.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_013.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_014.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_off_qntm_015.png
rename to core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_000.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_001.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_002.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_003.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_004.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_005.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_006.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_007.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_008.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_009.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_010.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_011.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_012.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_013.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_014.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_check_to_on_qntm_015.png
rename to core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_code_lock_default.png b/core/res/res/drawable-xhdpi/btn_code_lock_default.png
deleted file mode 100644
index c1358a2..0000000
--- a/core/res/res/drawable-xhdpi/btn_code_lock_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_code_lock_default_holo.png b/core/res/res/drawable-xhdpi/btn_code_lock_default_holo.png
deleted file mode 100644
index db1cbe6..0000000
--- a/core/res/res/drawable-xhdpi/btn_code_lock_default_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_code_lock_default_mtrl_alpha.png b/core/res/res/drawable-xhdpi/btn_code_lock_default_mtrl_alpha.png
new file mode 100644
index 0000000..0c457b4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_code_lock_default_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_code_lock_touched.png b/core/res/res/drawable-xhdpi/btn_code_lock_touched.png
deleted file mode 100644
index 0fafc3e..0000000
--- a/core/res/res/drawable-xhdpi/btn_code_lock_touched.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_code_lock_touched_holo.png b/core/res/res/drawable-xhdpi/btn_code_lock_touched_holo.png
deleted file mode 100644
index 073c3ac..0000000
--- a/core/res/res/drawable-xhdpi/btn_code_lock_touched_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_code_lock_touched_mtrl_alpha.png b/core/res/res/drawable-xhdpi/btn_code_lock_touched_mtrl_alpha.png
new file mode 100644
index 0000000..020d699
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_code_lock_touched_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/btn_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/btn_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_on_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/btn_radio_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_on_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_on_pressed_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/btn_radio_on_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_000.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_001.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_002.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_003.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_004.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_005.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_006.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_007.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_008.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_009.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_010.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_011.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_012.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_013.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_014.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_off_qntm_015.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_000.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_001.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_002.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_003.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_004.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_005.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_006.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_007.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_008.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_009.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_010.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_011.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_012.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_013.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_014.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_radio_to_on_qntm_015.png
rename to core/res/res/drawable-xhdpi/btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_rating_star_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_rating_star_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_rating_star_off_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/btn_rating_star_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_rating_star_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_rating_star_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_rating_star_on_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/btn_rating_star_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_star_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_star_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_star_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/btn_star_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_000.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_001.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_002.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_003.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_004.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_005.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_006.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_007.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_008.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_009.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_010.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_011.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_012.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_013.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_off_qntm_014.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_000.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_001.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_002.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_003.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_004.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_005.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_006.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_007.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_008.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_009.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_010.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_011.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_012.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_013.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_switch_to_on_qntm_014.png
rename to core/res/res/drawable-xhdpi/btn_switch_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/btn_toggle_indicator_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_toggle_indicator_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/btn_toggle_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/btn_toggle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/btn_toggle_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/btn_toggle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/dialog_background_mtrl_mult.9.png b/core/res/res/drawable-xhdpi/dialog_background_mtrl_mult.9.png
new file mode 100644
index 0000000..e6c0047
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/dialog_background_mtrl_mult.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/expander_close_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/expander_close_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/expander_close_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/expander_close_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/expander_open_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/expander_open_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/expander_open_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/expander_open_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_thumb_qntm_alpha.png b/core/res/res/drawable-xhdpi/fastscroll_thumb_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/fastscroll_thumb_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/fastscroll_thumb_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/fastscroll_track_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/fastscroll_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/fastscroll_track_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/fastscroll_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_ab_back_qntm_am_alpha.png b/core/res/res/drawable-xhdpi/ic_ab_back_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_ab_back_qntm_am_alpha.png
rename to core/res/res/drawable-xhdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_afw_icon.png b/core/res/res/drawable-xhdpi/ic_afw_icon.png
new file mode 100644
index 0000000..ec4f44b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_afw_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_cab_done_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_cab_done_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_cab_done_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_cab_done_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_clear_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_clear_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_clear_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_commit_search_api_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_commit_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_commit_search_api_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_commit_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_corp_badge.png b/core/res/res/drawable-xhdpi/ic_corp_badge.png
deleted file mode 100644
index 80d848d..0000000
--- a/core/res/res/drawable-xhdpi/ic_corp_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_dialog_alert_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_dialog_alert_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_dialog_alert_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_dialog_alert_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_find_next_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_find_next_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_find_next_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_find_next_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_find_previous_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_find_previous_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_find_previous_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_find_previous_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_go_search_api_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_go_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_go_search_api_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_go_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_disabled_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_disabled_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_media_route_disabled_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_media_route_disabled_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_media_route_off_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_media_route_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_0_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_on_0_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_media_route_on_0_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_media_route_on_0_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_1_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_on_1_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_media_route_on_1_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_media_route_on_1_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_2_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_on_2_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_media_route_on_2_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_media_route_on_2_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_media_route_on_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_media_route_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_copy_qntm_am_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_copy_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_menu_copy_qntm_am_alpha.png
rename to core/res/res/drawable-xhdpi/ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_cut_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_cut_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_menu_cut_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_find_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_find_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_menu_find_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_menu_find_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_menu_moreoverflow_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_paste_qntm_am_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_paste_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_menu_paste_qntm_am_alpha.png
rename to core/res/res/drawable-xhdpi/ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_search_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_search_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_menu_search_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_selectall_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_selectall_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_menu_selectall_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_menu_share_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_menu_share_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_menu_share_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_search_api_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_search_api_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_voice_search_api_qntm_alpha.png b/core/res/res/drawable-xhdpi/ic_voice_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/ic_voice_search_api_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index 2d34cf6..0000000
--- a/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png b/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
new file mode 100644
index 0000000..fda5e37
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default.png
deleted file mode 100644
index 0812cb5..0000000
--- a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default_holo.png b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default_holo.png
deleted file mode 100644
index 6a97445..0000000
--- a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default_mtrl_alpha.png b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default_mtrl_alpha.png
new file mode 100644
index 0000000..75d0221
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_default_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_green.png
deleted file mode 100644
index 3ab2e99..0000000
--- a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_green.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_green_holo.png b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_green_holo.png
deleted file mode 100644
index f0e9ab9..0000000
--- a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_green_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_mtrl_alpha.png b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_mtrl_alpha.png
new file mode 100644
index 0000000..225799b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_red_holo.png b/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_red_holo.png
deleted file mode 100644
index 170b833..0000000
--- a/core/res/res/drawable-xhdpi/indicator_code_lock_point_area_red_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_divider_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/list_divider_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/list_section_divider_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/list_section_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/list_section_divider_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/list_section_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_accessibility_features.png b/core/res/res/drawable-xhdpi/perm_group_accessibility_features.png
index 2fec7a3..53a12a1 100644
--- a/core/res/res/drawable-xhdpi/perm_group_accessibility_features.png
+++ b/core/res/res/drawable-xhdpi/perm_group_accessibility_features.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_accounts.png b/core/res/res/drawable-xhdpi/perm_group_accounts.png
index 74cd33b..ca622e9 100644
--- a/core/res/res/drawable-xhdpi/perm_group_accounts.png
+++ b/core/res/res/drawable-xhdpi/perm_group_accounts.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_affects_battery.png b/core/res/res/drawable-xhdpi/perm_group_affects_battery.png
index d4a9bb5d..3527aa7 100644
--- a/core/res/res/drawable-xhdpi/perm_group_affects_battery.png
+++ b/core/res/res/drawable-xhdpi/perm_group_affects_battery.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_app_info.png b/core/res/res/drawable-xhdpi/perm_group_app_info.png
index 46089e5..902b795 100644
--- a/core/res/res/drawable-xhdpi/perm_group_app_info.png
+++ b/core/res/res/drawable-xhdpi/perm_group_app_info.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_audio_settings.png b/core/res/res/drawable-xhdpi/perm_group_audio_settings.png
index 2f7cbc3..8100212 100644
--- a/core/res/res/drawable-xhdpi/perm_group_audio_settings.png
+++ b/core/res/res/drawable-xhdpi/perm_group_audio_settings.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_bluetooth.png b/core/res/res/drawable-xhdpi/perm_group_bluetooth.png
index 6bbde52..754da87 100644
--- a/core/res/res/drawable-xhdpi/perm_group_bluetooth.png
+++ b/core/res/res/drawable-xhdpi/perm_group_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_bookmarks.png b/core/res/res/drawable-xhdpi/perm_group_bookmarks.png
index 1277d03..44525bc 100644
--- a/core/res/res/drawable-xhdpi/perm_group_bookmarks.png
+++ b/core/res/res/drawable-xhdpi/perm_group_bookmarks.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_calendar.png b/core/res/res/drawable-xhdpi/perm_group_calendar.png
index 3c7f2d3..9821c27 100644
--- a/core/res/res/drawable-xhdpi/perm_group_calendar.png
+++ b/core/res/res/drawable-xhdpi/perm_group_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_camera.png b/core/res/res/drawable-xhdpi/perm_group_camera.png
index a454554..23b7167 100644
--- a/core/res/res/drawable-xhdpi/perm_group_camera.png
+++ b/core/res/res/drawable-xhdpi/perm_group_camera.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_device_alarms.png b/core/res/res/drawable-xhdpi/perm_group_device_alarms.png
index 1bb151c..615578e 100644
--- a/core/res/res/drawable-xhdpi/perm_group_device_alarms.png
+++ b/core/res/res/drawable-xhdpi/perm_group_device_alarms.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_display.png b/core/res/res/drawable-xhdpi/perm_group_display.png
index 9e36cf8..1489213 100644
--- a/core/res/res/drawable-xhdpi/perm_group_display.png
+++ b/core/res/res/drawable-xhdpi/perm_group_display.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_location.png b/core/res/res/drawable-xhdpi/perm_group_location.png
index 4c49521..d949cdb 100644
--- a/core/res/res/drawable-xhdpi/perm_group_location.png
+++ b/core/res/res/drawable-xhdpi/perm_group_location.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_messages.png b/core/res/res/drawable-xhdpi/perm_group_messages.png
index f046d46..dbb9aba 100644
--- a/core/res/res/drawable-xhdpi/perm_group_messages.png
+++ b/core/res/res/drawable-xhdpi/perm_group_messages.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_microphone.png b/core/res/res/drawable-xhdpi/perm_group_microphone.png
index bdb66e2..8887a41 100644
--- a/core/res/res/drawable-xhdpi/perm_group_microphone.png
+++ b/core/res/res/drawable-xhdpi/perm_group_microphone.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_network.png b/core/res/res/drawable-xhdpi/perm_group_network.png
index fe1adad..ebe034f 100644
--- a/core/res/res/drawable-xhdpi/perm_group_network.png
+++ b/core/res/res/drawable-xhdpi/perm_group_network.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_personal_info.png b/core/res/res/drawable-xhdpi/perm_group_personal_info.png
index 1ae418f..5ae4111 100644
--- a/core/res/res/drawable-xhdpi/perm_group_personal_info.png
+++ b/core/res/res/drawable-xhdpi/perm_group_personal_info.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_phone_calls.png b/core/res/res/drawable-xhdpi/perm_group_phone_calls.png
index 288e15c..9e1d2ca 100644
--- a/core/res/res/drawable-xhdpi/perm_group_phone_calls.png
+++ b/core/res/res/drawable-xhdpi/perm_group_phone_calls.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_screenlock.png b/core/res/res/drawable-xhdpi/perm_group_screenlock.png
index bf3ec34..96d6873 100644
--- a/core/res/res/drawable-xhdpi/perm_group_screenlock.png
+++ b/core/res/res/drawable-xhdpi/perm_group_screenlock.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_shortrange_network.png b/core/res/res/drawable-xhdpi/perm_group_shortrange_network.png
index 5e1e240..2f0c2d9 100644
--- a/core/res/res/drawable-xhdpi/perm_group_shortrange_network.png
+++ b/core/res/res/drawable-xhdpi/perm_group_shortrange_network.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_social_info.png b/core/res/res/drawable-xhdpi/perm_group_social_info.png
index 2111a83..842662c 100644
--- a/core/res/res/drawable-xhdpi/perm_group_social_info.png
+++ b/core/res/res/drawable-xhdpi/perm_group_social_info.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_status_bar.png b/core/res/res/drawable-xhdpi/perm_group_status_bar.png
index ce65380..8103320 100644
--- a/core/res/res/drawable-xhdpi/perm_group_status_bar.png
+++ b/core/res/res/drawable-xhdpi/perm_group_status_bar.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_storage.png b/core/res/res/drawable-xhdpi/perm_group_storage.png
index 4cd5c9b..a2d4d5e 100644
--- a/core/res/res/drawable-xhdpi/perm_group_storage.png
+++ b/core/res/res/drawable-xhdpi/perm_group_storage.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_sync_settings.png b/core/res/res/drawable-xhdpi/perm_group_sync_settings.png
index 24eb579..252a2a0 100644
--- a/core/res/res/drawable-xhdpi/perm_group_sync_settings.png
+++ b/core/res/res/drawable-xhdpi/perm_group_sync_settings.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_system_clock.png b/core/res/res/drawable-xhdpi/perm_group_system_clock.png
index 36d1294..da8a915 100644
--- a/core/res/res/drawable-xhdpi/perm_group_system_clock.png
+++ b/core/res/res/drawable-xhdpi/perm_group_system_clock.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_system_tools.png b/core/res/res/drawable-xhdpi/perm_group_system_tools.png
index 7b6cdd8..047c60c 100644
--- a/core/res/res/drawable-xhdpi/perm_group_system_tools.png
+++ b/core/res/res/drawable-xhdpi/perm_group_system_tools.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_user_dictionary.png b/core/res/res/drawable-xhdpi/perm_group_user_dictionary.png
index c0106bb..35d7d5f 100644
--- a/core/res/res/drawable-xhdpi/perm_group_user_dictionary.png
+++ b/core/res/res/drawable-xhdpi/perm_group_user_dictionary.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_user_dictionary_write.png b/core/res/res/drawable-xhdpi/perm_group_user_dictionary_write.png
index 36bb395..74e25ac 100644
--- a/core/res/res/drawable-xhdpi/perm_group_user_dictionary_write.png
+++ b/core/res/res/drawable-xhdpi/perm_group_user_dictionary_write.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_voicemail.png b/core/res/res/drawable-xhdpi/perm_group_voicemail.png
index eb17a63..430964d 100644
--- a/core/res/res/drawable-xhdpi/perm_group_voicemail.png
+++ b/core/res/res/drawable-xhdpi/perm_group_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/perm_group_wallpaper.png b/core/res/res/drawable-xhdpi/perm_group_wallpaper.png
index be4663c..3b698d8 100644
--- a/core/res/res/drawable-xhdpi/perm_group_wallpaper.png
+++ b/core/res/res/drawable-xhdpi/perm_group_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_background_qntm_mult.9.png b/core/res/res/drawable-xhdpi/popup_background_mtrl_mult.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/popup_background_qntm_mult.9.png
rename to core/res/res/drawable-xhdpi/popup_background_mtrl_mult.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/progress_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/progress_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/progress_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/progress_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/progress_primary_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/progress_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/progress_primary_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/progress_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrollbar_handle_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/scrollbar_handle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrollbar_handle_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/scrollbar_handle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_000.png
rename to core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_001.png
rename to core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_002.png
rename to core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_003.png
rename to core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_004.png
rename to core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_from_pressed_qntm_005.png
rename to core/res/res/drawable-xhdpi/scrubber_control_from_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_off_pressed_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_off_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_off_pressed_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/scrubber_control_off_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/scrubber_control_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_on_pressed_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_on_pressed_mtrl_alpha.png
similarity index 100%
copy from core/res/res/drawable-xhdpi/btn_radio_on_pressed_qntm_alpha.png
copy to core/res/res/drawable-xhdpi/scrubber_control_on_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_on_pressed_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_on_pressed_qntm_alpha.png
deleted file mode 100644
index a7ed0f8..0000000
--- a/core/res/res/drawable-xhdpi/scrubber_control_on_pressed_qntm_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_000.png
rename to core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_001.png
rename to core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_002.png
rename to core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_003.png
rename to core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_004.png
rename to core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_control_to_pressed_qntm_005.png
rename to core/res/res/drawable-xhdpi/scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_primary_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/scrubber_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_primary_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_track_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/scrubber_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/scrubber_track_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/scrubber_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png b/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
deleted file mode 100644
index 76e9428..0000000
--- a/core/res/res/drawable-xhdpi/spinner_20_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_20_outer_holo.png b/core/res/res/drawable-xhdpi/spinner_20_outer_holo.png
deleted file mode 100644
index 6f693d6..0000000
--- a/core/res/res/drawable-xhdpi/spinner_20_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_qntm_am_alpha.9.png b/core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/spinner_qntm_am_alpha.9.png
rename to core/res/res/drawable-xhdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/switch_track_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/tab_indicator_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/tab_indicator_mtrl_alpha.9.png
new file mode 100644
index 0000000..5610d8c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/tab_indicator_normal_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/tab_indicator_normal_qntm_alpha.9.png
deleted file mode 100644
index a677f9a..0000000
--- a/core/res/res/drawable-xhdpi/tab_indicator_normal_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/tab_indicator_selected_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/tab_indicator_selected_qntm_alpha.9.png
deleted file mode 100644
index 7de791d..0000000
--- a/core/res/res/drawable-xhdpi/tab_indicator_selected_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_cursor_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/text_cursor_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_left_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/text_select_handle_left_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_middle_qntm_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_middle_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/text_select_handle_middle_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/text_select_handle_middle_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-xhdpi/text_select_handle_right_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/text_select_handle_right_qntm_alpha.png
rename to core/res/res/drawable-xhdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_activated_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/textfield_activated_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_default_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/textfield_default_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_search_activated_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/textfield_search_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/textfield_search_activated_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/textfield_search_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/textfield_search_default_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/textfield_search_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/textfield_search_default_qntm_alpha.9.png
rename to core/res/res/drawable-xhdpi/textfield_search_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ab_share_pack_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/ab_share_pack_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ab_share_pack_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/ab_share_pack_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ab_solid_shadow_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/ab_solid_shadow_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ab_solid_shadow_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/ab_solid_shadow_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_cab_done_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_cab_done_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_cab_done_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/btn_cab_done_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_000.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_001.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_002.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_003.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_004.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_005.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_006.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_007.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_008.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_009.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_010.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_011.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_012.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_013.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_014.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_off_qntm_015.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_000.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_001.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_002.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_003.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_004.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_005.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_006.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_007.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_008.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_009.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_010.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_011.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_012.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_013.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_014.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_check_to_on_qntm_015.png
rename to core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_code_lock_default_holo.png b/core/res/res/drawable-xxhdpi/btn_code_lock_default_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_code_lock_default_holo.png
rename to core/res/res/drawable-xxhdpi/btn_code_lock_default_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_code_lock_touched_holo.png b/core/res/res/drawable-xxhdpi/btn_code_lock_touched_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_code_lock_touched_holo.png
rename to core/res/res/drawable-xxhdpi/btn_code_lock_touched_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/btn_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00000_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00001_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00002_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00003_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00004_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00005_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00006_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00007_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00008_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00009_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00010_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00011_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00012_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00013_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00014_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_radio_anim_00015_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_000.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_001.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_002.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_003.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_004.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_005.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_006.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_007.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_008.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_009.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_010.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_011.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_012.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_013.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_014.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_off_qntm_015.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_000.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_001.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_002.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_003.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_004.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_005.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_006.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_007.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_008.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_009.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_010.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_011.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_012.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_013.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_014.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_radio_to_on_qntm_015.png
rename to core/res/res/drawable-xxhdpi/btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_rating_star_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_rating_star_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_rating_star_off_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_rating_star_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_rating_star_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_rating_star_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_rating_star_on_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_rating_star_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_star_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_star_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_star_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/btn_star_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_000.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_001.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_002.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_003.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_004.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_005.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_006.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_007.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_008.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_009.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_010.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_011.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_012.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_013.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_off_qntm_014.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_000.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_001.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_002.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_003.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_004.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_005.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_006.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_007.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_008.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_009.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_010.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_011.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_012.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_013.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_switch_to_on_qntm_014.png
rename to core/res/res/drawable-xxhdpi/btn_switch_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_toggle_indicator_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_toggle_indicator_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/btn_toggle_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/btn_toggle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/btn_toggle_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/btn_toggle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/dialog_background_mtrl_mult.9.png b/core/res/res/drawable-xxhdpi/dialog_background_mtrl_mult.9.png
new file mode 100644
index 0000000..bb9debb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/dialog_background_mtrl_mult.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/expander_close_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/expander_close_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/expander_close_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/expander_close_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/expander_open_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/expander_open_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/expander_open_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/expander_open_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/fastscroll_thumb_qntm_alpha.png b/core/res/res/drawable-xxhdpi/fastscroll_thumb_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/fastscroll_thumb_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/fastscroll_thumb_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/fastscroll_track_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/fastscroll_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/fastscroll_track_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/fastscroll_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_ab_back_qntm_am_alpha.png b/core/res/res/drawable-xxhdpi/ic_ab_back_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_ab_back_qntm_am_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_ab_back_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_afw_icon.png b/core/res/res/drawable-xxhdpi/ic_afw_icon.png
new file mode 100644
index 0000000..6888377
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_afw_icon.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_cab_done_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_cab_done_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_cab_done_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_cab_done_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_clear_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_clear_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_clear_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_clear_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_commit_search_api_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_commit_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_commit_search_api_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_commit_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_corp_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_badge.png
deleted file mode 100644
index 885e2ac..0000000
--- a/core/res/res/drawable-xxhdpi/ic_corp_badge.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_dialog_alert_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_dialog_alert_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_dialog_alert_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_dialog_alert_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_find_next_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_find_next_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_find_next_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_find_next_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_find_previous_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_find_previous_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_find_previous_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_find_previous_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_go_search_api_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_go_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_go_search_api_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_go_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_disabled_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_disabled_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_media_route_disabled_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_media_route_disabled_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_media_route_off_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_media_route_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_media_route_on_0_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_media_route_on_0_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_media_route_on_1_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_media_route_on_1_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_media_route_on_2_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_media_route_on_2_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_media_route_on_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_media_route_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_copy_qntm_am_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_copy_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_menu_copy_qntm_am_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_menu_copy_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_cut_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_cut_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_menu_cut_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_menu_cut_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_find_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_find_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_menu_find_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_menu_find_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_moreoverflow_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_moreoverflow_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_menu_moreoverflow_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_menu_moreoverflow_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_paste_qntm_am_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_paste_mtrl_am_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_menu_paste_qntm_am_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_menu_paste_mtrl_am_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_search_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_search_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_menu_search_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_menu_search_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_selectall_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_selectall_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_menu_selectall_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_menu_selectall_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_menu_share_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_menu_share_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_menu_share_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_menu_share_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_search_api_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_search_api_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_voice_search_api_qntm_alpha.png b/core/res/res/drawable-xxhdpi/ic_voice_search_api_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/ic_voice_search_api_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/ic_voice_search_api_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
new file mode 100644
index 0000000..d3e80be
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_default_holo.png b/core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_default_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_default_holo.png
rename to core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_default_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_green_holo.png b/core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_green_holo.png
rename to core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_red_holo.png b/core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_red_holo.png
deleted file mode 100644
index f6c3e27..0000000
--- a/core/res/res/drawable-xxhdpi/indicator_code_lock_point_area_red_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/list_divider_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/list_divider_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/list_section_divider_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/list_section_divider_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/list_section_divider_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/list_section_divider_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_accessibility_features.png b/core/res/res/drawable-xxhdpi/perm_group_accessibility_features.png
new file mode 100644
index 0000000..5a63b68
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_accessibility_features.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_accounts.png b/core/res/res/drawable-xxhdpi/perm_group_accounts.png
new file mode 100644
index 0000000..2fc3baa
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_accounts.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_affects_battery.png b/core/res/res/drawable-xxhdpi/perm_group_affects_battery.png
new file mode 100644
index 0000000..63561be
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_affects_battery.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_app_info.png b/core/res/res/drawable-xxhdpi/perm_group_app_info.png
new file mode 100644
index 0000000..fc407f3
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_app_info.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_audio_settings.png b/core/res/res/drawable-xxhdpi/perm_group_audio_settings.png
new file mode 100644
index 0000000..23b5d97
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_audio_settings.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_bluetooth.png b/core/res/res/drawable-xxhdpi/perm_group_bluetooth.png
new file mode 100644
index 0000000..2dc9b23
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_bookmarks.png b/core/res/res/drawable-xxhdpi/perm_group_bookmarks.png
new file mode 100644
index 0000000..883bad3
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_bookmarks.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_calendar.png b/core/res/res/drawable-xxhdpi/perm_group_calendar.png
new file mode 100644
index 0000000..08f0474
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_camera.png b/core/res/res/drawable-xxhdpi/perm_group_camera.png
new file mode 100644
index 0000000..88a3d0e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_camera.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_device_alarms.png b/core/res/res/drawable-xxhdpi/perm_group_device_alarms.png
new file mode 100644
index 0000000..12ab22f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_device_alarms.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_display.png b/core/res/res/drawable-xxhdpi/perm_group_display.png
new file mode 100644
index 0000000..44e695e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_display.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_location.png b/core/res/res/drawable-xxhdpi/perm_group_location.png
new file mode 100644
index 0000000..3a83d8b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_location.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_messages.png b/core/res/res/drawable-xxhdpi/perm_group_messages.png
new file mode 100644
index 0000000..9e2ef73
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_messages.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_microphone.png b/core/res/res/drawable-xxhdpi/perm_group_microphone.png
new file mode 100644
index 0000000..65a6bf2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_microphone.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_network.png b/core/res/res/drawable-xxhdpi/perm_group_network.png
new file mode 100644
index 0000000..4bdb1ba
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_network.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_personal_info.png b/core/res/res/drawable-xxhdpi/perm_group_personal_info.png
new file mode 100644
index 0000000..c81a2a5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_personal_info.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_phone_calls.png b/core/res/res/drawable-xxhdpi/perm_group_phone_calls.png
new file mode 100644
index 0000000..e4daafb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_phone_calls.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_screenlock.png b/core/res/res/drawable-xxhdpi/perm_group_screenlock.png
new file mode 100644
index 0000000..3097363
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_screenlock.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_shortrange_network.png b/core/res/res/drawable-xxhdpi/perm_group_shortrange_network.png
new file mode 100644
index 0000000..6b21718
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_shortrange_network.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_social_info.png b/core/res/res/drawable-xxhdpi/perm_group_social_info.png
new file mode 100644
index 0000000..076fd19
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_social_info.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_status_bar.png b/core/res/res/drawable-xxhdpi/perm_group_status_bar.png
new file mode 100644
index 0000000..eda264b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_status_bar.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_storage.png b/core/res/res/drawable-xxhdpi/perm_group_storage.png
new file mode 100644
index 0000000..837211e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_storage.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_sync_settings.png b/core/res/res/drawable-xxhdpi/perm_group_sync_settings.png
new file mode 100644
index 0000000..15ab0fc
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_sync_settings.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_system_clock.png b/core/res/res/drawable-xxhdpi/perm_group_system_clock.png
new file mode 100644
index 0000000..9149497
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_system_clock.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_system_tools.png b/core/res/res/drawable-xxhdpi/perm_group_system_tools.png
new file mode 100644
index 0000000..0332e40
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_system_tools.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_user_dictionary.png b/core/res/res/drawable-xxhdpi/perm_group_user_dictionary.png
new file mode 100644
index 0000000..5b6ea3b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_user_dictionary.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_user_dictionary_write.png b/core/res/res/drawable-xxhdpi/perm_group_user_dictionary_write.png
new file mode 100644
index 0000000..d92e719
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_user_dictionary_write.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_voicemail.png b/core/res/res/drawable-xxhdpi/perm_group_voicemail.png
new file mode 100644
index 0000000..8f08516
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/perm_group_wallpaper.png b/core/res/res/drawable-xxhdpi/perm_group_wallpaper.png
new file mode 100644
index 0000000..9c87e9a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/perm_group_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/popup_background_qntm_mult.9.png b/core/res/res/drawable-xxhdpi/popup_background_mtrl_mult.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/popup_background_qntm_mult.9.png
rename to core/res/res/drawable-xxhdpi/popup_background_mtrl_mult.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/progress_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/progress_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/progress_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/progress_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/progress_primary_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/progress_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/progress_primary_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/progress_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrollbar_handle_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/scrollbar_handle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrollbar_handle_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/scrollbar_handle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_000.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_001.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_002.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_003.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_004.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_qntm_005.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_from_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_off_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_off_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_off_pressed_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_off_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_off_pressed_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_off_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_on_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_on_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_on_pressed_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_on_pressed_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_on_pressed_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_on_pressed_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_000.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_001.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_002.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_003.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_004.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_qntm_005.png
rename to core/res/res/drawable-xxhdpi/scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_primary_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/scrubber_primary_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_primary_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/scrubber_primary_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_track_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/scrubber_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/scrubber_track_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/scrubber_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/spinner_20_inner_holo.png b/core/res/res/drawable-xxhdpi/spinner_20_inner_holo.png
deleted file mode 100644
index 6cbd1f4..0000000
--- a/core/res/res/drawable-xxhdpi/spinner_20_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/spinner_20_outer_holo.png b/core/res/res/drawable-xxhdpi/spinner_20_outer_holo.png
deleted file mode 100644
index b6af5e7..0000000
--- a/core/res/res/drawable-xxhdpi/spinner_20_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/spinner_qntm_am_alpha.9.png b/core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/spinner_qntm_am_alpha.9.png
rename to core/res/res/drawable-xxhdpi/spinner_mtrl_am_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/switch_track_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/tab_indicator_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/tab_indicator_mtrl_alpha.9.png
new file mode 100644
index 0000000..248f4f8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/tab_indicator_normal_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/tab_indicator_normal_qntm_alpha.9.png
deleted file mode 100644
index 0a14025..0000000
--- a/core/res/res/drawable-xxhdpi/tab_indicator_normal_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/tab_indicator_selected_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/tab_indicator_selected_qntm_alpha.9.png
deleted file mode 100644
index 20e291a..0000000
--- a/core/res/res/drawable-xxhdpi/tab_indicator_selected_qntm_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_cursor_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/text_cursor_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_left_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/text_select_handle_left_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/text_select_handle_left_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_middle_qntm_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_middle_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/text_select_handle_middle_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/text_select_handle_middle_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png b/core/res/res/drawable-xxhdpi/text_select_handle_right_mtrl_alpha.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/text_select_handle_right_qntm_alpha.png
rename to core/res/res/drawable-xxhdpi/text_select_handle_right_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/textfield_activated_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/textfield_activated_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/textfield_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/textfield_default_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/textfield_default_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/textfield_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/textfield_search_activated_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/textfield_search_activated_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/textfield_search_activated_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/textfield_search_activated_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/textfield_search_default_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/textfield_search_default_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/textfield_search_default_qntm_alpha.9.png
rename to core/res/res/drawable-xxhdpi/textfield_search_default_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_000.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_001.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_002.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_003.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_004.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_005.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_006.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_007.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_008.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_009.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_010.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_011.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_012.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_013.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_014.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_off_qntm_015.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_000.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_001.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_002.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_003.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_004.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_005.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_006.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_007.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_008.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_009.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_010.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_011.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_012.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_013.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_014.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_check_to_on_qntm_015.png
rename to core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_qntm_alpha.9.png
rename to core/res/res/drawable-xxxhdpi/btn_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_000.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_001.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_002.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_003.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_004.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_005.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_006.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_007.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_008.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_009.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_010.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_011.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_012.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_013.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_014.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_off_qntm_015.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_off_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_000.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_001.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_002.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_003.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_004.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_005.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_006.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_007.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_008.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_009.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_010.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_011.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_012.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_013.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_014.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_015.png b/core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_015.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_radio_to_on_qntm_015.png
rename to core/res/res/drawable-xxxhdpi/btn_radio_to_on_mtrl_015.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_000.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_001.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_002.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_003.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_004.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_005.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_006.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_007.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_008.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_009.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_010.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_011.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_012.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_013.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_off_qntm_014.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_off_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_000.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_000.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_001.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_001.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_002.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_002.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_003.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_003.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_004.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_004.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_005.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_005.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_006.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_006.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_006.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_006.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_007.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_007.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_007.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_007.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_008.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_008.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_008.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_008.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_009.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_009.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_009.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_009.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_010.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_010.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_010.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_010.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_011.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_011.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_011.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_011.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_012.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_012.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_012.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_012.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_013.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_013.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_013.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_013.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_014.png b/core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_014.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_switch_to_on_qntm_014.png
rename to core/res/res/drawable-xxxhdpi/btn_switch_to_on_mtrl_014.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_toggle_indicator_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_toggle_indicator_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_toggle_indicator_qntm_alpha.9.png
rename to core/res/res/drawable-xxxhdpi/btn_toggle_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_toggle_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/btn_toggle_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/btn_toggle_qntm_alpha.9.png
rename to core/res/res/drawable-xxxhdpi/btn_toggle_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png b/core/res/res/drawable-xxxhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
new file mode 100644
index 0000000..23214fa
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/indicator_code_lock_drag_direction_up_mtrl_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_accessibility_features.png b/core/res/res/drawable-xxxhdpi/perm_group_accessibility_features.png
new file mode 100644
index 0000000..8cebecf
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_accessibility_features.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_accounts.png b/core/res/res/drawable-xxxhdpi/perm_group_accounts.png
new file mode 100644
index 0000000..1d9db83
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_accounts.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_affects_battery.png b/core/res/res/drawable-xxxhdpi/perm_group_affects_battery.png
new file mode 100644
index 0000000..3b6300a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_affects_battery.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_app_info.png b/core/res/res/drawable-xxxhdpi/perm_group_app_info.png
new file mode 100644
index 0000000..b54b98a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_app_info.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_audio_settings.png b/core/res/res/drawable-xxxhdpi/perm_group_audio_settings.png
new file mode 100644
index 0000000..ec88cdd
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_audio_settings.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_bluetooth.png b/core/res/res/drawable-xxxhdpi/perm_group_bluetooth.png
new file mode 100644
index 0000000..6f6409d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_bookmarks.png b/core/res/res/drawable-xxxhdpi/perm_group_bookmarks.png
new file mode 100644
index 0000000..f8f3f44
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_bookmarks.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_calendar.png b/core/res/res/drawable-xxxhdpi/perm_group_calendar.png
new file mode 100644
index 0000000..d6243b1
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_camera.png b/core/res/res/drawable-xxxhdpi/perm_group_camera.png
new file mode 100644
index 0000000..fdc4b44
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_camera.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_device_alarms.png b/core/res/res/drawable-xxxhdpi/perm_group_device_alarms.png
new file mode 100644
index 0000000..00707d4
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_device_alarms.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_display.png b/core/res/res/drawable-xxxhdpi/perm_group_display.png
new file mode 100644
index 0000000..ca4f44b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_display.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_location.png b/core/res/res/drawable-xxxhdpi/perm_group_location.png
new file mode 100644
index 0000000..a1019b2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_location.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_messages.png b/core/res/res/drawable-xxxhdpi/perm_group_messages.png
new file mode 100644
index 0000000..f7165fe
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_messages.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_microphone.png b/core/res/res/drawable-xxxhdpi/perm_group_microphone.png
new file mode 100644
index 0000000..a85e4cd
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_microphone.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_network.png b/core/res/res/drawable-xxxhdpi/perm_group_network.png
new file mode 100644
index 0000000..07f1eb7
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_network.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_personal_info.png b/core/res/res/drawable-xxxhdpi/perm_group_personal_info.png
new file mode 100644
index 0000000..11eb453
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_personal_info.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_phone_calls.png b/core/res/res/drawable-xxxhdpi/perm_group_phone_calls.png
new file mode 100644
index 0000000..f4e6b9f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_phone_calls.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_screenlock.png b/core/res/res/drawable-xxxhdpi/perm_group_screenlock.png
new file mode 100644
index 0000000..d559dce
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_screenlock.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_shortrange_network.png b/core/res/res/drawable-xxxhdpi/perm_group_shortrange_network.png
new file mode 100644
index 0000000..3998ab6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_shortrange_network.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_social_info.png b/core/res/res/drawable-xxxhdpi/perm_group_social_info.png
new file mode 100644
index 0000000..3b17e39
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_social_info.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_status_bar.png b/core/res/res/drawable-xxxhdpi/perm_group_status_bar.png
new file mode 100644
index 0000000..1b02702
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_status_bar.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_storage.png b/core/res/res/drawable-xxxhdpi/perm_group_storage.png
new file mode 100644
index 0000000..918b3ed
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_storage.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_sync_settings.png b/core/res/res/drawable-xxxhdpi/perm_group_sync_settings.png
new file mode 100644
index 0000000..12f90c5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_sync_settings.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_system_clock.png b/core/res/res/drawable-xxxhdpi/perm_group_system_clock.png
new file mode 100644
index 0000000..afd968b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_system_clock.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_system_tools.png b/core/res/res/drawable-xxxhdpi/perm_group_system_tools.png
new file mode 100644
index 0000000..dfcb702
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_system_tools.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_user_dictionary.png b/core/res/res/drawable-xxxhdpi/perm_group_user_dictionary.png
new file mode 100644
index 0000000..32942ca
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_user_dictionary.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_user_dictionary_write.png b/core/res/res/drawable-xxxhdpi/perm_group_user_dictionary_write.png
new file mode 100644
index 0000000..343551f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_user_dictionary_write.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_voicemail.png b/core/res/res/drawable-xxxhdpi/perm_group_voicemail.png
new file mode 100644
index 0000000..7aeb786
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/perm_group_wallpaper.png b/core/res/res/drawable-xxxhdpi/perm_group_wallpaper.png
new file mode 100644
index 0000000..3c08471
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/perm_group_wallpaper.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_000.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_000.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_001.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_001.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_002.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_002.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_003.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_003.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_004.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_004.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_005.png b/core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_qntm_005.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_from_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_000.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_000.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_000.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_000.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_001.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_001.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_001.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_001.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_002.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_002.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_002.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_002.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_003.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_003.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_003.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_003.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_004.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_004.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_004.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_004.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_005.png b/core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_005.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_qntm_005.png
rename to core/res/res/drawable-xxxhdpi/scrubber_control_to_pressed_mtrl_005.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/switch_track_qntm_alpha.9.png b/core/res/res/drawable-xxxhdpi/switch_track_mtrl_alpha.9.png
similarity index 100%
rename from core/res/res/drawable-xxxhdpi/switch_track_qntm_alpha.9.png
rename to core/res/res/drawable-xxxhdpi/switch_track_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/tab_indicator_mtrl_alpha.9.png b/core/res/res/drawable-xxxhdpi/tab_indicator_mtrl_alpha.9.png
new file mode 100644
index 0000000..5813179
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/tab_indicator_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/ab_share_pack_material.xml b/core/res/res/drawable/ab_share_pack_material.xml
new file mode 100644
index 0000000..1f0478e
--- /dev/null
+++ b/core/res/res/drawable/ab_share_pack_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ab_share_pack_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ab_share_pack_quantum.xml b/core/res/res/drawable/ab_share_pack_quantum.xml
deleted file mode 100644
index 7d33ff4d..0000000
--- a/core/res/res/drawable/ab_share_pack_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ab_share_pack_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ab_solid_shadow_material.xml b/core/res/res/drawable/ab_solid_shadow_material.xml
new file mode 100644
index 0000000..eee52c8
--- /dev/null
+++ b/core/res/res/drawable/ab_solid_shadow_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ab_solid_shadow_mtrl_alpha"
+ android:tint="@color/black" />
diff --git a/core/res/res/drawable/ab_solid_shadow_quantum.xml b/core/res/res/drawable/ab_solid_shadow_quantum.xml
deleted file mode 100644
index 88e142a..0000000
--- a/core/res/res/drawable/ab_solid_shadow_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ab_solid_shadow_qntm_alpha"
- android:tint="@color/black" />
diff --git a/core/res/res/drawable/activated_background_quantum.xml b/core/res/res/drawable/activated_background_material.xml
similarity index 100%
rename from core/res/res/drawable/activated_background_quantum.xml
rename to core/res/res/drawable/activated_background_material.xml
diff --git a/core/res/res/drawable/btn_borderless_material.xml b/core/res/res/drawable/btn_borderless_material.xml
new file mode 100644
index 0000000..a459089
--- /dev/null
+++ b/core/res/res/drawable/btn_borderless_material.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight">
+ <item android:id="@id/mask"
+ android:drawable="@drawable/btn_mtrl_alpha" />
+</ripple>
diff --git a/core/res/res/drawable/btn_borderless_quantum.xml b/core/res/res/drawable/btn_borderless_quantum.xml
deleted file mode 100644
index 2cd7ed6..0000000
--- a/core/res/res/drawable/btn_borderless_quantum.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?attr/colorControlHighlight">
- <item android:id="@id/mask"
- android:drawable="@drawable/btn_qntm_alpha" />
-</ripple>
diff --git a/core/res/res/drawable/btn_cab_done_material.xml b/core/res/res/drawable/btn_cab_done_material.xml
new file mode 100644
index 0000000..36cc196
--- /dev/null
+++ b/core/res/res/drawable/btn_cab_done_material.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true">
+ <item android:state_pressed="true">
+ <color android:color="?attr/colorControlHighlight" />
+ </item>
+ <item android:state_focused="true" android:state_enabled="true">
+ <nine-patch android:src="@drawable/btn_cab_done_mtrl_alpha"
+ android:tint="?attr/colorControlHighlight" />
+ </item>
+ <item android:state_enabled="true">
+ <nine-patch android:src="@drawable/btn_cab_done_mtrl_alpha"
+ android:tint="?attr/colorButtonNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/btn_cab_done_quantum.xml b/core/res/res/drawable/btn_cab_done_quantum.xml
deleted file mode 100644
index 51e06bb..0000000
--- a/core/res/res/drawable/btn_cab_done_quantum.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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"
- android:autoMirrored="true">
- <item android:state_pressed="true">
- <color android:color="?attr/colorControlHighlight" />
- </item>
- <item android:state_focused="true" android:state_enabled="true">
- <nine-patch android:src="@drawable/btn_cab_done_qntm_alpha"
- android:tint="?attr/colorControlHighlight" />
- </item>
- <item android:state_enabled="true">
- <nine-patch android:src="@drawable/btn_cab_done_qntm_alpha"
- android:tint="?attr/colorButtonNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/btn_check_material_anim.xml b/core/res/res/drawable/btn_check_material_anim.xml
new file mode 100644
index 0000000..73b8a3e
--- /dev/null
+++ b/core/res/res/drawable/btn_check_material_anim.xml
@@ -0,0 +1,135 @@
+<?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.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:state_checked="true">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_015" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_000" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_checked="true" android:id="@+id/on">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_015" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:id="@+id/off">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_000" android:tint="?attr/colorControlNormal" />
+ </item>
+ <transition android:fromId="@+id/off" android:toId="@+id/on">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_000" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_001" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_002" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_003" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_004" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_005" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_006" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_007" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_008" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_009" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_010" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_011" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_012" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_013" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_014" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_on_mtrl_015" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+ <transition android:fromId="@+id/on" android:toId="@+id/off">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_000" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_001" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_002" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_003" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_004" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_005" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_006" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_007" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_008" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_009" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_010" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_011" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_012" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_013" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_014" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_check_to_off_mtrl_015" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+</animated-selector>
+
diff --git a/core/res/res/drawable/btn_check_quantum_anim.xml b/core/res/res/drawable/btn_check_quantum_anim.xml
deleted file mode 100644
index b16875e..0000000
--- a/core/res/res/drawable/btn_check_quantum_anim.xml
+++ /dev/null
@@ -1,135 +0,0 @@
-<?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.
--->
-
-<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:state_checked="true">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_enabled="false">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_checked="true" android:id="@+id/on">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:id="@+id/off">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlNormal" />
- </item>
- <transition android:fromId="@+id/off" android:toId="@+id/on">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_000" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_001" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_002" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_003" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_004" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_005" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_006" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_007" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_008" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_009" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_010" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_011" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_012" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_013" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_014" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
- <transition android:fromId="@+id/on" android:toId="@+id/off">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_000" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_001" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_002" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_003" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_004" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_005" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_006" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_007" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_008" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_009" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_010" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_011" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_012" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_013" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_014" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_check_to_off_qntm_015" android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
-</animated-selector>
-
diff --git a/core/res/res/drawable/btn_default_material.xml b/core/res/res/drawable/btn_default_material.xml
new file mode 100644
index 0000000..9cee3ab
--- /dev/null
+++ b/core/res/res/drawable/btn_default_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight">
+ <item>
+ <nine-patch android:src="@drawable/btn_mtrl_alpha"
+ android:tint="?attr/colorButtonNormal" />
+ </item>
+</ripple>
diff --git a/core/res/res/drawable/btn_default_quantum.xml b/core/res/res/drawable/btn_default_quantum.xml
deleted file mode 100644
index 61193fe..0000000
--- a/core/res/res/drawable/btn_default_quantum.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?attr/colorControlHighlight">
- <item>
- <nine-patch android:src="@drawable/btn_qntm_alpha"
- android:tint="?attr/colorButtonNormal" />
- </item>
-</ripple>
diff --git a/core/res/res/drawable/btn_radio_material_anim.xml b/core/res/res/drawable/btn_radio_material_anim.xml
new file mode 100644
index 0000000..0be590e
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_material_anim.xml
@@ -0,0 +1,134 @@
+<?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.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false" android:state_checked="true">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_015" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_000" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_checked="true" android:id="@+id/on">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_015" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:id="@+id/off">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_000" android:tint="?attr/colorControlNormal" />
+ </item>
+ <transition android:fromId="@+id/off" android:toId="@+id/on">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_000" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_001" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_002" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_003" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_004" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_005" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_006" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_007" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_008" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_009" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_010" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_011" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_012" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_013" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_014" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_on_mtrl_015" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+ <transition android:fromId="@+id/on" android:toId="@+id/off">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_000" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_001" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_002" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_003" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_004" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_005" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_006" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_007" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_008" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_009" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_010" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_011" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_012" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_013" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_014" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_radio_to_off_mtrl_015" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/btn_radio_quantum_anim.xml b/core/res/res/drawable/btn_radio_quantum_anim.xml
deleted file mode 100644
index cd9b518..0000000
--- a/core/res/res/drawable/btn_radio_quantum_anim.xml
+++ /dev/null
@@ -1,134 +0,0 @@
-<?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.
--->
-
-<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:state_checked="true">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_enabled="false">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_checked="true" android:id="@+id/on">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:id="@+id/off">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlNormal" />
- </item>
- <transition android:fromId="@+id/off" android:toId="@+id/on">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_000" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_001" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_002" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_003" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_004" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_005" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_006" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_007" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_008" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_009" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_010" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_011" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_012" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_013" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_014" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_on_qntm_015" android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
- <transition android:fromId="@+id/on" android:toId="@+id/off">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_000" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_001" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_002" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_003" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_004" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_005" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_006" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_007" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_008" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_009" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_010" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_011" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_012" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_013" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_014" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_radio_to_off_qntm_015" android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
-</animated-selector>
diff --git a/core/res/res/drawable/btn_star_material.xml b/core/res/res/drawable/btn_star_material.xml
new file mode 100644
index 0000000..29862d2
--- /dev/null
+++ b/core/res/res/drawable/btn_star_material.xml
@@ -0,0 +1,30 @@
+<?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_checked="true">
+ <bitmap android:src="@drawable/btn_star_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:state_pressed="true">
+ <bitmap android:src="@drawable/btn_star_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item>
+ <bitmap android:src="@drawable/btn_star_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/btn_star_quantum.xml b/core/res/res/drawable/btn_star_quantum.xml
deleted file mode 100644
index 512cd57..0000000
--- a/core/res/res/drawable/btn_star_quantum.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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_checked="true">
- <bitmap android:src="@drawable/btn_star_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_pressed="true">
- <bitmap android:src="@drawable/btn_star_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <bitmap android:src="@drawable/btn_star_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/btn_toggle_material.xml b/core/res/res/drawable/btn_toggle_material.xml
new file mode 100644
index 0000000..73fe4d3
--- /dev/null
+++ b/core/res/res/drawable/btn_toggle_material.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="4dp"
+ android:insetTop="4dp"
+ android:insetBottom="4dp"
+ android:insetRight="4dp">
+ <layer-list android:paddingMode="stack">
+ <item>
+ <ripple android:color="?attr/colorControlHighlight">
+ <item>
+ <nine-patch android:src="@drawable/btn_toggle_mtrl_alpha"
+ android:tint="?attr/colorButtonNormal" />
+ </item>
+ </ripple>
+ </item>
+ <item>
+ <selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="false">
+ <nine-patch android:src="@drawable/btn_toggle_indicator_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_checked="true">
+ <nine-patch android:src="@drawable/btn_toggle_indicator_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ </selector>
+ </item>
+ </layer-list>
+</inset>
diff --git a/core/res/res/drawable/btn_toggle_quantum.xml b/core/res/res/drawable/btn_toggle_quantum.xml
deleted file mode 100644
index e235598..0000000
--- a/core/res/res/drawable/btn_toggle_quantum.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetLeft="4dp"
- android:insetTop="4dp"
- android:insetBottom="4dp"
- android:insetRight="4dp">
- <layer-list android:paddingMode="stack">
- <item>
- <ripple android:tint="?attr/colorControlHighlight">
- <item>
- <nine-patch android:src="@drawable/btn_toggle_qntm_alpha"
- android:tint="?attr/colorButtonNormal" />
- </item>
- </ripple>
- </item>
- <item>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_checked="false">
- <nine-patch android:src="@drawable/btn_toggle_indicator_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_checked="true">
- <nine-patch android:src="@drawable/btn_toggle_indicator_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- </selector>
- </item>
- </layer-list>
-</inset>
diff --git a/core/res/res/drawable/dialog_background_quantum.xml b/core/res/res/drawable/dialog_background_material.xml
similarity index 100%
rename from core/res/res/drawable/dialog_background_quantum.xml
rename to core/res/res/drawable/dialog_background_material.xml
diff --git a/core/res/res/drawable/dialog_background_shadow_material.xml b/core/res/res/drawable/dialog_background_shadow_material.xml
new file mode 100644
index 0000000..0554920
--- /dev/null
+++ b/core/res/res/drawable/dialog_background_shadow_material.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/dialog_background_mtrl_mult"
+ android:tint="?attr/colorBackground"
+ android:tintMode="multiply" />
diff --git a/core/res/res/drawable/edit_text_material.xml b/core/res/res/drawable/edit_text_material.xml
new file mode 100644
index 0000000..86cec8f
--- /dev/null
+++ b/core/res/res/drawable/edit_text_material.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlActivated">
+ <item>
+ <selector>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/textfield_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ </selector>
+ </item>
+ <item android:id="@+id/mask" android:drawable="@drawable/textfield_activated_mtrl_alpha" />
+</ripple>
diff --git a/core/res/res/drawable/edit_text_quantum.xml b/core/res/res/drawable/edit_text_quantum.xml
deleted file mode 100644
index 67339e8..0000000
--- a/core/res/res/drawable/edit_text_quantum.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?attr/colorControlActivated">
- <item>
- <selector>
- <item android:state_window_focused="false">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_enabled="false">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_focused="false" android:state_activated="false">
- <nine-patch android:src="@drawable/textfield_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item>
- <nine-patch android:src="@drawable/textfield_activated_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- </selector>
- </item>
-</ripple>
diff --git a/core/res/res/drawable/expander_group_material.xml b/core/res/res/drawable/expander_group_material.xml
new file mode 100644
index 0000000..aa41796
--- /dev/null
+++ b/core/res/res/drawable/expander_group_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_expanded="true">
+ <nine-patch android:src="@drawable/expander_close_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/expander_open_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/expander_group_quantum.xml b/core/res/res/drawable/expander_group_quantum.xml
deleted file mode 100644
index 48245ea..0000000
--- a/core/res/res/drawable/expander_group_quantum.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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_expanded="true">
- <nine-patch android:src="@drawable/expander_close_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item>
- <nine-patch android:src="@drawable/expander_open_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/fastscroll_thumb_material.xml b/core/res/res/drawable/fastscroll_thumb_material.xml
new file mode 100644
index 0000000..1288f0d
--- /dev/null
+++ b/core/res/res/drawable/fastscroll_thumb_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <bitmap android:src="@drawable/fastscroll_thumb_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item>
+ <bitmap android:src="@drawable/fastscroll_thumb_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/fastscroll_thumb_quantum.xml b/core/res/res/drawable/fastscroll_thumb_quantum.xml
deleted file mode 100644
index 496b2ae..0000000
--- a/core/res/res/drawable/fastscroll_thumb_quantum.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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">
- <bitmap android:src="@drawable/fastscroll_thumb_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <bitmap android:src="@drawable/fastscroll_thumb_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/fastscroll_track_material.xml b/core/res/res/drawable/fastscroll_track_material.xml
new file mode 100644
index 0000000..60f79b1
--- /dev/null
+++ b/core/res/res/drawable/fastscroll_track_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/fastscroll_track_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/fastscroll_track_quantum.xml b/core/res/res/drawable/fastscroll_track_quantum.xml
deleted file mode 100644
index 59c35db..0000000
--- a/core/res/res/drawable/fastscroll_track_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/fastscroll_track_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_ab_back_material.xml b/core/res/res/drawable/ic_ab_back_material.xml
new file mode 100644
index 0000000..37455d4
--- /dev/null
+++ b/core/res/res/drawable/ic_ab_back_material.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_ab_back_mtrl_am_alpha"
+ android:autoMirrored="true"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_ab_back_quantum.xml b/core/res/res/drawable/ic_ab_back_quantum.xml
deleted file mode 100644
index 65c7584..0000000
--- a/core/res/res/drawable/ic_ab_back_quantum.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_ab_back_qntm_am_alpha"
- android:autoMirrored="true"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_cab_done_material.xml b/core/res/res/drawable/ic_cab_done_material.xml
new file mode 100644
index 0000000..a370288
--- /dev/null
+++ b/core/res/res/drawable/ic_cab_done_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_cab_done_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_cab_done_quantum.xml b/core/res/res/drawable/ic_cab_done_quantum.xml
deleted file mode 100644
index 97495a8..0000000
--- a/core/res/res/drawable/ic_cab_done_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_cab_done_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_clear_material.xml b/core/res/res/drawable/ic_clear_material.xml
new file mode 100644
index 0000000..076c0a2
--- /dev/null
+++ b/core/res/res/drawable/ic_clear_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_clear_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_clear_quantum.xml b/core/res/res/drawable/ic_clear_quantum.xml
deleted file mode 100644
index 02f0929..0000000
--- a/core/res/res/drawable/ic_clear_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_clear_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_commit_search_api_material.xml b/core/res/res/drawable/ic_commit_search_api_material.xml
new file mode 100644
index 0000000..59bd0fa
--- /dev/null
+++ b/core/res/res/drawable/ic_commit_search_api_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_commit_search_api_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_commit_search_api_quantum.xml b/core/res/res/drawable/ic_commit_search_api_quantum.xml
deleted file mode 100644
index 02d08b9..0000000
--- a/core/res/res/drawable/ic_commit_search_api_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_commit_search_api_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
new file mode 100644
index 0000000..5325712
--- /dev/null
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -0,0 +1,34 @@
+<!--
+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="19.0dp"
+ android:height="19.0dp"/>
+
+ <viewport
+ android:viewportWidth="19.0"
+ android:viewportHeight="19.0"/>
+
+ <path
+ android:pathData="M9.5,9.5m-9.5,0.0a9.5,9.5 0.0,1.0 1.0,19.0 0.0a9.5,9.5 0.0,1.0 1.0,-19.0 0.0"
+ android:fill="#FF5722"/>
+ <path
+ android:pathData="M12.667,7.125l-1.583,0.0L11.084,6.333l-0.792,-0.792L8.708,5.5410004L7.917,6.333l0.0,0.792L6.333,7.125c-0.438,0.0 -0.788,0.354 -0.788,0.792l-0.004,4.354c0.0,0.438 0.354,0.792 0.792,0.792l6.333,0.0c0.438,0.0 0.792,-0.354 0.792,-0.792L13.458,7.917C13.458,7.479 13.104,7.125 12.667,7.125zM10.094,10.687L8.906,10.687L8.906,9.5l1.188,0.0L10.094,10.687zM10.292,7.125L8.708,7.125L8.708,6.333l1.583,0.0L10.291,7.125z"
+ android:fill="#FFFFFF"/>
+ <path
+ android:pathData="M4.75,4.75 h9.5 v9.5 h-9.5z"
+ android:fill="#00000000"/>
+</vector>
diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge.xml
new file mode 100644
index 0000000..7bfab4c
--- /dev/null
+++ b/core/res/res/drawable/ic_corp_icon_badge.xml
@@ -0,0 +1,40 @@
+<!--
+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="64.0dp"
+ android:height="64.0dp"/>
+
+ <viewport
+ android:viewportWidth="64.0"
+ android:viewportHeight="64.0"/>
+
+ <path
+ android:fill="#FF000000"
+ android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
+ <path
+ android:fill="#FF000000"
+ android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
+ <path
+ android:pathData="M49.0,49.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
+ android:fill="#FF5722"/>
+ <path
+ android:pathData="M53.667,45.5l-2.333,0.0l0.0,-1.167l-1.167,-1.167l-2.333,0.0l-1.167,1.167L46.667,45.5l-2.333,0.0c-0.645,0.0 -1.161,0.522 -1.161,1.167l-0.006,6.417c0.0,0.645 0.522,1.167 1.167,1.167l9.333,0.0c0.645,0.0 1.167,-0.522 1.167,-1.167l0.0,-6.417C54.833,46.022 54.311,45.5 53.667,45.5zM49.875,50.75l-1.75,0.0L48.125,49.0l1.75,0.0L49.875,50.75zM50.167,45.5l-2.333,0.0l0.0,-1.167l2.333,0.0L50.167,45.5z"
+ android:fill="#FFFFFF"/>
+ <path
+ android:pathData="M42.0,42.0 h14.0 v14.0 h-14.0z"
+ android:fill="#00000000"/>
+</vector>
diff --git a/core/res/res/drawable/ic_dialog_alert_material.xml b/core/res/res/drawable/ic_dialog_alert_material.xml
new file mode 100644
index 0000000..41e1ab1
--- /dev/null
+++ b/core/res/res/drawable/ic_dialog_alert_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_dialog_alert_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_dialog_alert_quantum.xml b/core/res/res/drawable/ic_dialog_alert_quantum.xml
deleted file mode 100644
index 05f3630..0000000
--- a/core/res/res/drawable/ic_dialog_alert_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_dialog_alert_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_find_next_material.xml b/core/res/res/drawable/ic_find_next_material.xml
new file mode 100644
index 0000000..c6674eb
--- /dev/null
+++ b/core/res/res/drawable/ic_find_next_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_find_next_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_find_next_quantum.xml b/core/res/res/drawable/ic_find_next_quantum.xml
deleted file mode 100644
index fee196c..0000000
--- a/core/res/res/drawable/ic_find_next_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_find_next_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_find_previous_material.xml b/core/res/res/drawable/ic_find_previous_material.xml
new file mode 100644
index 0000000..32fcb31
--- /dev/null
+++ b/core/res/res/drawable/ic_find_previous_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_find_previous_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_find_previous_quantum.xml b/core/res/res/drawable/ic_find_previous_quantum.xml
deleted file mode 100644
index 28f887a..0000000
--- a/core/res/res/drawable/ic_find_previous_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_find_previous_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_go_search_api_material.xml b/core/res/res/drawable/ic_go_search_api_material.xml
new file mode 100644
index 0000000..03f6cd5
--- /dev/null
+++ b/core/res/res/drawable/ic_go_search_api_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_go_search_api_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_go_search_api_quantum.xml b/core/res/res/drawable/ic_go_search_api_quantum.xml
deleted file mode 100644
index b5b5cfb..0000000
--- a/core/res/res/drawable/ic_go_search_api_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_go_search_api_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_media_route_connecting_material.xml b/core/res/res/drawable/ic_media_route_connecting_material.xml
new file mode 100644
index 0000000..51decd3
--- /dev/null
+++ b/core/res/res/drawable/ic_media_route_connecting_material.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:oneshot="false">
+ <item android:duration="500">
+ <bitmap android:src="@drawable/ic_media_route_on_0_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:duration="500">
+ <bitmap android:src="@drawable/ic_media_route_on_1_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:duration="500">
+ <bitmap android:src="@drawable/ic_media_route_on_2_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:duration="500">
+ <bitmap android:src="@drawable/ic_media_route_on_1_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</animation-list>
diff --git a/core/res/res/drawable/ic_media_route_connecting_quantum.xml b/core/res/res/drawable/ic_media_route_connecting_quantum.xml
deleted file mode 100644
index 0029dd4..0000000
--- a/core/res/res/drawable/ic_media_route_connecting_quantum.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:oneshot="false">
- <item android:duration="500">
- <bitmap android:src="@drawable/ic_media_route_on_0_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="500">
- <bitmap android:src="@drawable/ic_media_route_on_1_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="500">
- <bitmap android:src="@drawable/ic_media_route_on_2_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:duration="500">
- <bitmap android:src="@drawable/ic_media_route_on_1_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</animation-list>
diff --git a/core/res/res/drawable/ic_media_route_material.xml b/core/res/res/drawable/ic_media_route_material.xml
new file mode 100644
index 0000000..3e3f388
--- /dev/null
+++ b/core/res/res/drawable/ic_media_route_material.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true" android:state_enabled="true"
+ android:drawable="@android:drawable/ic_media_route_connecting_material" />
+ <item android:state_activated="true" android:state_enabled="true">
+ <bitmap android:src="@android:drawable/ic_media_route_on_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_enabled="true">
+ <bitmap android:src="@android:drawable/ic_media_route_off_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item>
+ <bitmap android:src="@android:drawable/ic_media_route_disabled_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/ic_media_route_quantum.xml b/core/res/res/drawable/ic_media_route_quantum.xml
deleted file mode 100644
index 16b63d4..0000000
--- a/core/res/res/drawable/ic_media_route_quantum.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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_checked="true" android:state_enabled="true"
- android:drawable="@android:drawable/ic_media_route_connecting_quantum" />
- <item android:state_activated="true" android:state_enabled="true">
- <bitmap android:src="@android:drawable/ic_media_route_on_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_enabled="true">
- <bitmap android:src="@android:drawable/ic_media_route_off_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item>
- <bitmap android:src="@android:drawable/ic_media_route_disabled_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/ic_menu_copy_material.xml b/core/res/res/drawable/ic_menu_copy_material.xml
new file mode 100644
index 0000000..877b5ff
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_copy_material.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_menu_copy_mtrl_am_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true" />
diff --git a/core/res/res/drawable/ic_menu_copy_quantum.xml b/core/res/res/drawable/ic_menu_copy_quantum.xml
deleted file mode 100644
index 5f6ce51..0000000
--- a/core/res/res/drawable/ic_menu_copy_quantum.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_copy_qntm_am_alpha"
- android:tint="?attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/core/res/res/drawable/ic_menu_cut_material.xml b/core/res/res/drawable/ic_menu_cut_material.xml
new file mode 100644
index 0000000..ff8d6e6
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_cut_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_menu_cut_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_cut_quantum.xml b/core/res/res/drawable/ic_menu_cut_quantum.xml
deleted file mode 100644
index 1e4996e..0000000
--- a/core/res/res/drawable/ic_menu_cut_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_cut_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_find_material.xml b/core/res/res/drawable/ic_menu_find_material.xml
new file mode 100644
index 0000000..00b294e
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_find_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_menu_find_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_find_quantum.xml b/core/res/res/drawable/ic_menu_find_quantum.xml
deleted file mode 100644
index a69c673..0000000
--- a/core/res/res/drawable/ic_menu_find_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_find_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_moreoverflow_material.xml b/core/res/res/drawable/ic_menu_moreoverflow_material.xml
new file mode 100644
index 0000000..16d4f0c
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_moreoverflow_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_menu_moreoverflow_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_moreoverflow_quantum.xml b/core/res/res/drawable/ic_menu_moreoverflow_quantum.xml
deleted file mode 100644
index 7d3fcac..0000000
--- a/core/res/res/drawable/ic_menu_moreoverflow_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_moreoverflow_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_paste_material.xml b/core/res/res/drawable/ic_menu_paste_material.xml
new file mode 100644
index 0000000..f7bbbf9
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_paste_material.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_menu_paste_mtrl_am_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:autoMirrored="true" />
diff --git a/core/res/res/drawable/ic_menu_paste_quantum.xml b/core/res/res/drawable/ic_menu_paste_quantum.xml
deleted file mode 100644
index f590904..0000000
--- a/core/res/res/drawable/ic_menu_paste_quantum.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_paste_qntm_am_alpha"
- android:tint="?attr/colorControlNormal"
- android:autoMirrored="true" />
diff --git a/core/res/res/drawable/ic_menu_search_material.xml b/core/res/res/drawable/ic_menu_search_material.xml
new file mode 100644
index 0000000..78dd62f
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_search_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_menu_search_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_search_quantum.xml b/core/res/res/drawable/ic_menu_search_quantum.xml
deleted file mode 100644
index 2ca8c20..0000000
--- a/core/res/res/drawable/ic_menu_search_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_search_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_selectall_material.xml b/core/res/res/drawable/ic_menu_selectall_material.xml
new file mode 100644
index 0000000..a431dd5
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_selectall_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_menu_selectall_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_selectall_quantum.xml b/core/res/res/drawable/ic_menu_selectall_quantum.xml
deleted file mode 100644
index fd72ebf..0000000
--- a/core/res/res/drawable/ic_menu_selectall_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_selectall_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_share_material.xml b/core/res/res/drawable/ic_menu_share_material.xml
new file mode 100644
index 0000000..d9153af
--- /dev/null
+++ b/core/res/res/drawable/ic_menu_share_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_menu_share_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_menu_share_quantum.xml b/core/res/res/drawable/ic_menu_share_quantum.xml
deleted file mode 100644
index f44e06c..0000000
--- a/core/res/res/drawable/ic_menu_share_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_menu_share_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_search_api_material.xml b/core/res/res/drawable/ic_search_api_material.xml
new file mode 100644
index 0000000..bc18398
--- /dev/null
+++ b/core/res/res/drawable/ic_search_api_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_search_api_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_search_api_quantum.xml b/core/res/res/drawable/ic_search_api_quantum.xml
deleted file mode 100644
index 2bbc294..0000000
--- a/core/res/res/drawable/ic_search_api_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_search_api_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_voice_search_api_material.xml b/core/res/res/drawable/ic_voice_search_api_material.xml
new file mode 100644
index 0000000..05488fb
--- /dev/null
+++ b/core/res/res/drawable/ic_voice_search_api_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_voice_search_api_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/ic_voice_search_api_quantum.xml b/core/res/res/drawable/ic_voice_search_api_quantum.xml
deleted file mode 100644
index ddb14ef..0000000
--- a/core/res/res/drawable/ic_voice_search_api_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_voice_search_api_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/item_background_borderless_material.xml b/core/res/res/drawable/item_background_borderless_material.xml
new file mode 100644
index 0000000..b730618
--- /dev/null
+++ b/core/res/res/drawable/item_background_borderless_material.xml
@@ -0,0 +1,18 @@
+<?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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight" />
diff --git a/core/res/res/drawable/item_background_borderless_quantum.xml b/core/res/res/drawable/item_background_borderless_quantum.xml
deleted file mode 100644
index c2a1c127..0000000
--- a/core/res/res/drawable/item_background_borderless_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?attr/colorControlHighlight"
- android:pinned="true" />
diff --git a/core/res/res/drawable/item_background_material.xml b/core/res/res/drawable/item_background_material.xml
new file mode 100644
index 0000000..45ff181
--- /dev/null
+++ b/core/res/res/drawable/item_background_material.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlHighlight">
+ <item android:id="@id/mask">
+ <color android:color="@color/white" />
+ </item>
+</ripple>
diff --git a/core/res/res/drawable/item_background_quantum.xml b/core/res/res/drawable/item_background_quantum.xml
deleted file mode 100644
index 039ca51..0000000
--- a/core/res/res/drawable/item_background_quantum.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?attr/colorControlHighlight">
- <item android:id="@id/mask">
- <color android:color="@color/white" />
- </item>
-</ripple>
\ No newline at end of file
diff --git a/core/res/res/drawable/list_divider_material.xml b/core/res/res/drawable/list_divider_material.xml
new file mode 100644
index 0000000..bf24933
--- /dev/null
+++ b/core/res/res/drawable/list_divider_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/list_divider_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/list_divider_quantum.xml b/core/res/res/drawable/list_divider_quantum.xml
deleted file mode 100644
index e3d4ba2..0000000
--- a/core/res/res/drawable/list_divider_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/list_divider_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/list_section_divider_material.xml b/core/res/res/drawable/list_section_divider_material.xml
new file mode 100644
index 0000000..515634e
--- /dev/null
+++ b/core/res/res/drawable/list_section_divider_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/list_section_divider_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/list_section_divider_quantum.xml b/core/res/res/drawable/list_section_divider_quantum.xml
deleted file mode 100644
index 87a1439..0000000
--- a/core/res/res/drawable/list_section_divider_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/list_section_divider_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/notification_bg_dim.xml b/core/res/res/drawable/notification_bg_dim.xml
index ae03d82..5c245f8 100644
--- a/core/res/res/drawable/notification_bg_dim.xml
+++ b/core/res/res/drawable/notification_bg_dim.xml
@@ -15,9 +15,7 @@
~ limitations under the License
-->
-<ripple
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="#ff444444"
- >
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="#ff444444">
<item android:drawable="@drawable/notification_bg_normal" />
</ripple>
\ No newline at end of file
diff --git a/core/res/res/drawable/notification_material_bg.xml b/core/res/res/drawable/notification_material_bg.xml
new file mode 100644
index 0000000..44c67be
--- /dev/null
+++ b/core/res/res/drawable/notification_material_bg.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <shape>
+ <solid android:color="#ffd0d0d0" />
+ <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
+ </shape>
+ </item>
+ <item>
+ <shape>
+ <solid android:color="#fffafafa" />
+ <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
+ </shape>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable/notification_material_bg_dim.xml b/core/res/res/drawable/notification_material_bg_dim.xml
new file mode 100644
index 0000000..9b691e6
--- /dev/null
+++ b/core/res/res/drawable/notification_material_bg_dim.xml
@@ -0,0 +1,32 @@
+<?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
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/white">
+ <item>
+ <shape>
+ <solid android:color="#d4ffffff" />
+ <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
+ </shape>
+ </item>
+ <item android:id="@id/mask">
+ <shape>
+ <solid android:color="@color/white" />
+ <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
+ </shape>
+ </item>
+</ripple>
diff --git a/core/res/res/drawable/notification_quantum_media_progress.xml b/core/res/res/drawable/notification_material_media_progress.xml
similarity index 100%
rename from core/res/res/drawable/notification_quantum_media_progress.xml
rename to core/res/res/drawable/notification_material_media_progress.xml
diff --git a/core/res/res/drawable/notification_quantum_bg.xml b/core/res/res/drawable/notification_quantum_bg.xml
deleted file mode 100644
index 300a565..0000000
--- a/core/res/res/drawable/notification_quantum_bg.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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">
- <shape>
- <solid android:color="#ffd0d0d0" />
- <corners android:radius="@dimen/notification_quantum_rounded_rect_radius" />
- </shape>
- </item>
- <item>
- <shape>
- <solid android:color="#fffafafa" />
- <corners android:radius="@dimen/notification_quantum_rounded_rect_radius" />
- </shape>
- </item>
-</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable/notification_quantum_bg_dim.xml b/core/res/res/drawable/notification_quantum_bg_dim.xml
deleted file mode 100644
index eb9a4ab..0000000
--- a/core/res/res/drawable/notification_quantum_bg_dim.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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
- -->
-
-<ripple
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="#ffffffff"
- android:tintMode="src_over"
- >
- <item>
- <shape>
- <solid android:color="#d4ffffff" />
- <corners android:radius="@dimen/notification_quantum_rounded_rect_radius" />
- </shape>
- </item>
-</ripple>
\ No newline at end of file
diff --git a/core/res/res/drawable/popup_background_material.xml b/core/res/res/drawable/popup_background_material.xml
new file mode 100644
index 0000000..9e50790
--- /dev/null
+++ b/core/res/res/drawable/popup_background_material.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/popup_background_mtrl_mult"
+ android:tint="?attr/colorBackground"
+ android:tintMode="multiply" />
diff --git a/core/res/res/drawable/popup_background_quantum.xml b/core/res/res/drawable/popup_background_quantum.xml
deleted file mode 100644
index a4d0291..0000000
--- a/core/res/res/drawable/popup_background_quantum.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/popup_background_qntm_mult"
- android:tint="?attr/colorBackground"
- android:tintMode="multiply" />
diff --git a/core/res/res/drawable/progress_horizontal_material.xml b/core/res/res/drawable/progress_horizontal_material.xml
new file mode 100644
index 0000000..eca1a24
--- /dev/null
+++ b/core/res/res/drawable/progress_horizontal_material.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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@id/background">
+ <nine-patch android:src="@drawable/progress_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:id="@id/secondaryProgress">
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/progress_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </scale>
+ </item>
+ <item android:id="@id/progress">
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/progress_primary_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </scale>
+ </item>
+</layer-list>
diff --git a/core/res/res/drawable/progress_horizontal_quantum.xml b/core/res/res/drawable/progress_horizontal_quantum.xml
deleted file mode 100644
index 1c2d494..0000000
--- a/core/res/res/drawable/progress_horizontal_quantum.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@id/background">
- <nine-patch android:src="@drawable/progress_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:id="@id/secondaryProgress">
- <scale android:scaleWidth="100%">
- <nine-patch android:src="@drawable/progress_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </scale>
- </item>
- <item android:id="@id/progress">
- <scale android:scaleWidth="100%">
- <nine-patch android:src="@drawable/progress_primary_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </scale>
- </item>
-</layer-list>
diff --git a/core/res/res/drawable/progress_large_material.xml b/core/res/res/drawable/progress_large_material.xml
new file mode 100644
index 0000000..965b288
--- /dev/null
+++ b/core/res/res/drawable/progress_large_material.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<material-progress xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlActivated"
+ android:width="76dp"
+ android:height="76dp"
+ android:thickness="6.3dp"
+ android:innerRadius="30.1dp" />
diff --git a/core/res/res/drawable/progress_medium_material.xml b/core/res/res/drawable/progress_medium_material.xml
new file mode 100644
index 0000000..c656026
--- /dev/null
+++ b/core/res/res/drawable/progress_medium_material.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<material-progress xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlActivated"
+ android:width="48dp"
+ android:height="48dp"
+ android:thickness="4dp"
+ android:innerRadius="19dp" />
diff --git a/core/res/res/drawable/progress_small_material.xml b/core/res/res/drawable/progress_small_material.xml
new file mode 100644
index 0000000..67ae268
--- /dev/null
+++ b/core/res/res/drawable/progress_small_material.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<material-progress xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?attr/colorControlActivated"
+ android:width="16dp"
+ android:height="16dp"
+ android:thickness="1.3dp"
+ android:innerRadius="6.3dp" />
diff --git a/core/res/res/drawable/ratingbar_full_empty_material.xml b/core/res/res/drawable/ratingbar_full_empty_material.xml
new file mode 100644
index 0000000..a2ae7d9
--- /dev/null
+++ b/core/res/res/drawable/ratingbar_full_empty_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <bitmap android:src="@drawable/btn_rating_star_off_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item>
+ <bitmap android:src="@drawable/btn_rating_star_off_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/ratingbar_full_empty_quantum.xml b/core/res/res/drawable/ratingbar_full_empty_quantum.xml
deleted file mode 100644
index e5e4315..0000000
--- a/core/res/res/drawable/ratingbar_full_empty_quantum.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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">
- <bitmap android:src="@drawable/btn_rating_star_off_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <bitmap android:src="@drawable/btn_rating_star_off_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/ratingbar_full_filled_material.xml b/core/res/res/drawable/ratingbar_full_filled_material.xml
new file mode 100644
index 0000000..801c85f
--- /dev/null
+++ b/core/res/res/drawable/ratingbar_full_filled_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <bitmap android:src="@drawable/btn_rating_star_on_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item>
+ <bitmap android:src="@drawable/btn_rating_star_on_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/ratingbar_full_filled_quantum.xml b/core/res/res/drawable/ratingbar_full_filled_quantum.xml
deleted file mode 100644
index ad3aa5d..0000000
--- a/core/res/res/drawable/ratingbar_full_filled_quantum.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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">
- <bitmap android:src="@drawable/btn_rating_star_on_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <bitmap android:src="@drawable/btn_rating_star_on_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/ratingbar_full_material.xml b/core/res/res/drawable/ratingbar_full_material.xml
new file mode 100644
index 0000000..122dd1d
--- /dev/null
+++ b/core/res/res/drawable/ratingbar_full_material.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@id/background"
+ android:drawable="@drawable/ratingbar_full_empty_material" />
+ <item android:id="@id/secondaryProgress"
+ android:drawable="@drawable/ratingbar_full_empty_material" />
+ <item android:id="@id/progress"
+ android:drawable="@drawable/ratingbar_full_filled_material" />
+</layer-list>
diff --git a/core/res/res/drawable/ratingbar_full_quantum.xml b/core/res/res/drawable/ratingbar_full_quantum.xml
deleted file mode 100644
index 143e7c2..0000000
--- a/core/res/res/drawable/ratingbar_full_quantum.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
--->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@id/background"
- android:drawable="@drawable/ratingbar_full_empty_quantum" />
- <item android:id="@id/secondaryProgress"
- android:drawable="@drawable/ratingbar_full_empty_quantum" />
- <item android:id="@id/progress"
- android:drawable="@drawable/ratingbar_full_filled_quantum" />
-</layer-list>
diff --git a/core/res/res/drawable/scrollbar_handle_material.xml b/core/res/res/drawable/scrollbar_handle_material.xml
new file mode 100644
index 0000000..a241428
--- /dev/null
+++ b/core/res/res/drawable/scrollbar_handle_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/scrollbar_handle_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/scrollbar_handle_quantum.xml b/core/res/res/drawable/scrollbar_handle_quantum.xml
deleted file mode 100644
index f2de252..0000000
--- a/core/res/res/drawable/scrollbar_handle_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/scrollbar_handle_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
diff --git a/core/res/res/drawable/scrubber_control_material_anim.xml b/core/res/res/drawable/scrubber_control_material_anim.xml
new file mode 100644
index 0000000..4b13259
--- /dev/null
+++ b/core/res/res/drawable/scrubber_control_material_anim.xml
@@ -0,0 +1,74 @@
+<?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.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
+ <item android:state_enabled="false" android:state_pressed="true">
+ <bitmap android:src="@drawable/scrubber_control_off_mtrl_alpha" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <bitmap android:src="@drawable/scrubber_control_off_mtrl_alpha" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_pressed="true" android:id="@+id/pressed">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_mtrl_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:id="@+id/not_pressed">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_mtrl_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <transition android:fromId="@+id/not_pressed" android:toId="@+id/pressed">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_mtrl_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_mtrl_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_mtrl_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_mtrl_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_mtrl_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_to_pressed_mtrl_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+ <transition android:fromId="@+id/pressed" android:toId="@+id/not_pressed">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_mtrl_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_mtrl_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_mtrl_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_mtrl_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_mtrl_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/scrubber_control_from_pressed_mtrl_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/scrubber_control_quantum_anim.xml b/core/res/res/drawable/scrubber_control_quantum_anim.xml
deleted file mode 100644
index 87d3ae9..0000000
--- a/core/res/res/drawable/scrubber_control_quantum_anim.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?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.
--->
-
-<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
- <item android:state_enabled="false" android:state_pressed="true">
- <bitmap android:src="@drawable/scrubber_control_off_qntm_alpha" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_enabled="false">
- <bitmap android:src="@drawable/scrubber_control_off_qntm_alpha" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_pressed="true" android:id="@+id/pressed">
- <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:id="@+id/not_pressed">
- <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <transition android:fromId="@+id/not_pressed" android:toId="@+id/pressed">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_to_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
- <transition android:fromId="@+id/pressed" android:toId="@+id/not_pressed">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/scrubber_control_from_pressed_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
-</animated-selector>
diff --git a/core/res/res/drawable/scrubber_control_selector_material.xml b/core/res/res/drawable/scrubber_control_selector_material.xml
new file mode 100644
index 0000000..9892676
--- /dev/null
+++ b/core/res/res/drawable/scrubber_control_selector_material.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false">
+ <bitmap android:src="@drawable/scrubber_control_off_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item>
+ <bitmap android:src="@drawable/scrubber_control_on_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/scrubber_control_selector_quantum.xml b/core/res/res/drawable/scrubber_control_selector_quantum.xml
deleted file mode 100644
index ef3af0c..0000000
--- a/core/res/res/drawable/scrubber_control_selector_quantum.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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_enabled="false">
- <bitmap android:src="@drawable/scrubber_control_off_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item>
- <bitmap android:src="@drawable/scrubber_control_on_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_material.xml b/core/res/res/drawable/scrubber_progress_horizontal_material.xml
new file mode 100644
index 0000000..f2ea30f
--- /dev/null
+++ b/core/res/res/drawable/scrubber_progress_horizontal_material.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/scrubber_track_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item>
+ <layer-list>
+ <item android:id="@id/background">
+ <nine-patch android:src="@drawable/scrubber_track_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:id="@id/secondaryProgress">
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/scrubber_primary_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </scale>
+ </item>
+ <item android:id="@id/progress">
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/scrubber_primary_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </scale>
+ </item>
+ </layer-list>
+ </item>
+</selector>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
deleted file mode 100644
index f82fe7a..0000000
--- a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?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_enabled="false">
- <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item>
- <layer-list>
- <item android:id="@id/background">
- <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:id="@id/secondaryProgress">
- <scale android:scaleWidth="100%">
- <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </scale>
- </item>
- <item android:id="@id/progress">
- <scale android:scaleWidth="100%">
- <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </scale>
- </item>
- </layer-list>
- </item>
-</selector>
diff --git a/core/res/res/drawable/spinner_background_material.xml b/core/res/res/drawable/spinner_background_material.xml
new file mode 100644
index 0000000..02ea11b
--- /dev/null
+++ b/core/res/res/drawable/spinner_background_material.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true">
+ <item android:state_checked="true">
+ <nine-patch android:src="@drawable/spinner_mtrl_am_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:state_pressed="true">
+ <nine-patch android:src="@drawable/spinner_mtrl_am_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/spinner_mtrl_am_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/spinner_background_quantum.xml b/core/res/res/drawable/spinner_background_quantum.xml
deleted file mode 100644
index 727a78f..0000000
--- a/core/res/res/drawable/spinner_background_quantum.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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"
- android:autoMirrored="true">
- <item android:state_checked="true">
- <nine-patch android:src="@drawable/spinner_qntm_am_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_pressed="true">
- <nine-patch android:src="@drawable/spinner_qntm_am_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <nine-patch android:src="@drawable/spinner_qntm_am_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/switch_thumb_material_anim.xml b/core/res/res/drawable/switch_thumb_material_anim.xml
new file mode 100644
index 0000000..e7baa2c
--- /dev/null
+++ b/core/res/res/drawable/switch_thumb_material_anim.xml
@@ -0,0 +1,128 @@
+<?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.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
+ <item android:state_enabled="false" android:state_checked="true">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_checked="true" android:id="@+id/on">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:id="@+id/off">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlNormal" />
+ </item>
+ <transition android:fromId="@+id/off" android:toId="@+id/on">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_on_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+ <transition android:fromId="@+id/on" android:toId="@+id/off">
+ <animation-list>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="15">
+ <bitmap android:src="@drawable/btn_switch_to_off_mtrl_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/switch_thumb_quantum_anim.xml b/core/res/res/drawable/switch_thumb_quantum_anim.xml
deleted file mode 100644
index 1984d47..0000000
--- a/core/res/res/drawable/switch_thumb_quantum_anim.xml
+++ /dev/null
@@ -1,128 +0,0 @@
-<?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.
--->
-
-<animated-selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
- <item android:state_enabled="false" android:state_checked="true">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_enabled="false">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_000" android:gravity="center" android:tint="?attr/colorControlNormal" android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_checked="true" android:id="@+id/on">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:id="@+id/off">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_000" android:gravity="center" android:tint="?attr/colorControlNormal" />
- </item>
- <transition android:fromId="@+id/off" android:toId="@+id/on">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_on_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
- <transition android:fromId="@+id/on" android:toId="@+id/off">
- <animation-list>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_000" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_001" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_002" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_003" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_004" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_005" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_006" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_007" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_008" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_009" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_010" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_011" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_012" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_013" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- <item android:duration="15">
- <bitmap android:src="@drawable/btn_switch_to_off_qntm_014" android:gravity="center" android:tint="?attr/colorControlActivated" />
- </item>
- </animation-list>
- </transition>
-</animated-selector>
diff --git a/core/res/res/drawable/switch_track_material.xml b/core/res/res/drawable/switch_track_material.xml
new file mode 100644
index 0000000..6ca2489
--- /dev/null
+++ b/core/res/res/drawable/switch_track_material.xml
@@ -0,0 +1,36 @@
+<?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_enabled="false" android:state_checked="true">
+ <nine-patch android:src="@drawable/switch_track_mtrl_alpha"
+ android:tint="?attr/colorControlActivated"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/switch_track_mtrl_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_checked="true">
+ <nine-patch android:src="@drawable/switch_track_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/switch_track_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/switch_track_quantum.xml b/core/res/res/drawable/switch_track_quantum.xml
deleted file mode 100644
index 3651a0a..0000000
--- a/core/res/res/drawable/switch_track_quantum.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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_enabled="false" android:state_checked="true">
- <nine-patch android:src="@drawable/switch_track_qntm_alpha"
- android:tint="?attr/colorControlActivated"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_enabled="false">
- <nine-patch android:src="@drawable/switch_track_qntm_alpha"
- android:tint="?attr/colorControlNormal"
- android:alpha="?attr/disabledAlpha" />
- </item>
- <item android:state_checked="true">
- <nine-patch android:src="@drawable/switch_track_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <nine-patch android:src="@drawable/switch_track_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/tab_indicator_material.xml b/core/res/res/drawable/tab_indicator_material.xml
new file mode 100644
index 0000000..16362c0
--- /dev/null
+++ b/core/res/res/drawable/tab_indicator_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true">
+ <nine-patch android:src="@drawable/tab_indicator_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:drawable="@color/transparent" />
+</selector>
diff --git a/core/res/res/drawable/tab_indicator_quantum.xml b/core/res/res/drawable/tab_indicator_quantum.xml
deleted file mode 100644
index 6fabe23..0000000
--- a/core/res/res/drawable/tab_indicator_quantum.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?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_selected="true" android:state_pressed="true">
- <nine-patch android:src="@drawable/tab_indicator_selected_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_selected="true" android:state_focused="true">
- <nine-patch android:src="@drawable/tab_indicator_selected_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_selected="true">
- <nine-patch android:src="@drawable/tab_indicator_selected_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-
- <item android:state_pressed="true">
- <nine-patch android:src="@drawable/tab_indicator_normal_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_focused="true">
- <nine-patch android:src="@drawable/tab_indicator_normal_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item>
- <nine-patch android:src="@drawable/tab_indicator_normal_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/drawable/text_cursor_material.xml b/core/res/res/drawable/text_cursor_material.xml
new file mode 100644
index 0000000..a350c47
--- /dev/null
+++ b/core/res/res/drawable/text_cursor_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/text_cursor_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/text_cursor_quantum.xml b/core/res/res/drawable/text_cursor_quantum.xml
deleted file mode 100644
index 23d4ae0..0000000
--- a/core/res/res/drawable/text_cursor_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/text_cursor_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/text_select_handle_left_material.xml b/core/res/res/drawable/text_select_handle_left_material.xml
new file mode 100644
index 0000000..b228d3f
--- /dev/null
+++ b/core/res/res/drawable/text_select_handle_left_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/text_select_handle_left_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/text_select_handle_left_quantum.xml b/core/res/res/drawable/text_select_handle_left_quantum.xml
deleted file mode 100644
index a0ad7cf..0000000
--- a/core/res/res/drawable/text_select_handle_left_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/text_select_handle_left_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/text_select_handle_middle_material.xml b/core/res/res/drawable/text_select_handle_middle_material.xml
new file mode 100644
index 0000000..f0f4b3e
--- /dev/null
+++ b/core/res/res/drawable/text_select_handle_middle_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/text_select_handle_middle_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/text_select_handle_middle_quantum.xml b/core/res/res/drawable/text_select_handle_middle_quantum.xml
deleted file mode 100644
index bff0b66..0000000
--- a/core/res/res/drawable/text_select_handle_middle_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/text_select_handle_middle_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/text_select_handle_right_material.xml b/core/res/res/drawable/text_select_handle_right_material.xml
new file mode 100644
index 0000000..acacbf6
--- /dev/null
+++ b/core/res/res/drawable/text_select_handle_right_material.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/text_select_handle_right_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/text_select_handle_right_quantum.xml b/core/res/res/drawable/text_select_handle_right_quantum.xml
deleted file mode 100644
index 413661f..0000000
--- a/core/res/res/drawable/text_select_handle_right_quantum.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/text_select_handle_right_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
diff --git a/core/res/res/drawable/textfield_search_material.xml b/core/res/res/drawable/textfield_search_material.xml
new file mode 100644
index 0000000..1c0f5eb
--- /dev/null
+++ b/core/res/res/drawable/textfield_search_material.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false" android:state_enabled="true">
+ <nine-patch android:src="@drawable/textfield_search_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_window_focused="false" android:state_enabled="false">
+ <nine-patch android:src="@drawable/textfield_search_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item android:state_enabled="true" android:state_focused="true">
+ <nine-patch android:src="@drawable/textfield_search_activated_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:state_enabled="true" android:state_activated="true">
+ <nine-patch android:src="@drawable/textfield_search_activated_mtrl_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:state_enabled="true">
+ <nine-patch android:src="@drawable/textfield_search_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/textfield_search_default_mtrl_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+</selector>
diff --git a/core/res/res/drawable/textfield_search_quantum.xml b/core/res/res/drawable/textfield_search_quantum.xml
deleted file mode 100644
index 877de46..0000000
--- a/core/res/res/drawable/textfield_search_quantum.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?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_window_focused="false" android:state_enabled="true">
- <nine-patch android:src="@drawable/textfield_search_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_window_focused="false" android:state_enabled="false">
- <nine-patch android:src="@drawable/textfield_search_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item android:state_enabled="true" android:state_focused="true">
- <nine-patch android:src="@drawable/textfield_search_activated_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_enabled="true" android:state_activated="true">
- <nine-patch android:src="@drawable/textfield_search_activated_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
- </item>
- <item android:state_enabled="true">
- <nine-patch android:src="@drawable/textfield_search_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
- <item>
- <nine-patch android:src="@drawable/textfield_search_default_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
- </item>
-</selector>
diff --git a/core/res/res/interpolator/accelerate_quart.xml b/core/res/res/interpolator/accelerate_quart.xml
new file mode 100644
index 0000000..64efec6
--- /dev/null
+++ b/core/res/res/interpolator/accelerate_quart.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:factor="2.0" />
diff --git a/core/res/res/interpolator/decelerate_quart.xml b/core/res/res/interpolator/decelerate_quart.xml
new file mode 100644
index 0000000..9f6a51f
--- /dev/null
+++ b/core/res/res/interpolator/decelerate_quart.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:factor="2.0" />
diff --git a/core/res/res/layout/action_bar_home_quantum.xml b/core/res/res/layout/action_bar_home_material.xml
similarity index 100%
rename from core/res/res/layout/action_bar_home_quantum.xml
rename to core/res/res/layout/action_bar_home_material.xml
diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml
new file mode 100644
index 0000000..57bdfc9
--- /dev/null
+++ b/core/res/res/layout/alert_dialog_material.xml
@@ -0,0 +1,122 @@
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/parentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@drawable/dialog_background_material"
+ android:translationZ="@dimen/floating_window_z"
+ android:layout_marginLeft="@dimen/floating_window_margin_left"
+ android:layout_marginTop="@dimen/floating_window_margin_top"
+ android:layout_marginRight="@dimen/floating_window_margin_right"
+ android:layout_marginBottom="@dimen/floating_window_margin_bottom">
+
+ <LinearLayout android:id="@+id/topPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout android:id="@+id/title_template"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:paddingTop="16dip">
+ <ImageView android:id="@+id/icon"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginEnd="8dip"
+ android:scaleType="fitCenter"
+ android:src="@null" />
+ <TextView android:id="@+id/alertTitle"
+ style="?attr/windowTitleStyle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart" />
+ </LinearLayout>
+ <!-- If the client uses a customTitle, it will be added here. -->
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/contentPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:minHeight="64dp">
+ <ScrollView android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+ <TextView android:id="@+id/message"
+ style="?attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:paddingTop="16dip" />
+ </ScrollView>
+ </LinearLayout>
+
+ <FrameLayout android:id="@+id/customPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:minHeight="64dp">
+ <FrameLayout android:id="@+android:id/custom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </FrameLayout>
+
+ <LinearLayout android:id="@+id/buttonPanel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/alert_dialog_button_bar_height"
+ android:orientation="vertical"
+ android:gravity="end"
+ android:padding="16dip">
+ <LinearLayout
+ style="?attr/buttonBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layoutDirection="locale">
+ <Button android:id="@+id/button3"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
+ <Button android:id="@+id/button2"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
+ <Button android:id="@+id/button1"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:minHeight="@dimen/alert_dialog_button_bar_height" />
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/alert_dialog_progress_quantum.xml b/core/res/res/layout/alert_dialog_progress_material.xml
similarity index 100%
rename from core/res/res/layout/alert_dialog_progress_quantum.xml
rename to core/res/res/layout/alert_dialog_progress_material.xml
diff --git a/core/res/res/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml
deleted file mode 100644
index 7fd22ad..0000000
--- a/core/res/res/layout/alert_dialog_quantum.xml
+++ /dev/null
@@ -1,122 +0,0 @@
-<?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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/parentPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/dialog_background_quantum"
- android:translationZ="@dimen/floating_window_z"
- android:layout_marginLeft="@dimen/floating_window_margin_left"
- android:layout_marginTop="@dimen/floating_window_margin_top"
- android:layout_marginRight="@dimen/floating_window_margin_right"
- android:layout_marginBottom="@dimen/floating_window_margin_bottom">
-
- <LinearLayout android:id="@+id/topPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <LinearLayout android:id="@+id/title_template"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical|start"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="16dip">
- <ImageView android:id="@+id/icon"
- android:layout_width="32dip"
- android:layout_height="32dip"
- android:layout_marginEnd="8dip"
- android:scaleType="fitCenter"
- android:src="@null" />
- <TextView android:id="@+id/alertTitle"
- style="?attr/windowTitleStyle"
- android:singleLine="true"
- android:ellipsize="end"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="viewStart" />
- </LinearLayout>
- <!-- If the client uses a customTitle, it will be added here. -->
- </LinearLayout>
-
- <LinearLayout android:id="@+id/contentPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:minHeight="64dp">
- <ScrollView android:id="@+id/scrollView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipToPadding="false">
- <TextView android:id="@+id/message"
- style="?attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="16dip" />
- </ScrollView>
- </LinearLayout>
-
- <FrameLayout android:id="@+id/customPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:minHeight="64dp">
- <FrameLayout android:id="@+android:id/custom"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </FrameLayout>
-
- <LinearLayout android:id="@+id/buttonPanel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- android:orientation="vertical"
- android:gravity="end"
- android:padding="16dip">
- <LinearLayout
- style="?attr/buttonBarStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layoutDirection="locale">
- <Button android:id="@+id/button3"
- style="?attr/buttonBarButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height" />
- <Button android:id="@+id/button2"
- style="?attr/buttonBarButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height" />
- <Button android:id="@+id/button1"
- style="?attr/buttonBarButtonStyle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height" />
- </LinearLayout>
- </LinearLayout>
-</LinearLayout>
diff --git a/core/res/res/layout/dialog_custom_title_material.xml b/core/res/res/layout/dialog_custom_title_material.xml
new file mode 100644
index 0000000..550b72e
--- /dev/null
+++ b/core/res/res/layout/dialog_custom_title_material.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+This is a custom layout for a dialog.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:fitsSystemWindows="true">
+ <FrameLayout android:id="@android:id/title_container"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/windowTitleSize"
+ android:layout_weight="0"
+ android:gravity="center_vertical|start"
+ style="?android:attr/windowTitleBackgroundStyle" />
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:foreground="?android:attr/windowContentOverlay">
+ <FrameLayout android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </FrameLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/dialog_custom_title_quantum.xml b/core/res/res/layout/dialog_custom_title_quantum.xml
deleted file mode 100644
index 1bb93eb..0000000
--- a/core/res/res/layout/dialog_custom_title_quantum.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?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.
--->
-
-<!--
-This is a custom layout for a dialog.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:fitsSystemWindows="true">
- <FrameLayout android:id="@android:id/title_container"
- android:layout_width="match_parent"
- android:layout_weight="0"
- android:gravity="center_vertical|start"
- style="?android:attr/windowTitleBackgroundStyle" />
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:foreground="?android:attr/windowContentOverlay">
- <FrameLayout android:id="@android:id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </FrameLayout>
-</LinearLayout>
diff --git a/core/res/res/layout/dialog_title_icons_quantum.xml b/core/res/res/layout/dialog_title_icons_material.xml
similarity index 100%
rename from core/res/res/layout/dialog_title_icons_quantum.xml
rename to core/res/res/layout/dialog_title_icons_material.xml
diff --git a/core/res/res/layout/dialog_title_material.xml b/core/res/res/layout/dialog_title_material.xml
new file mode 100644
index 0000000..918c8f1
--- /dev/null
+++ b/core/res/res/layout/dialog_title_material.xml
@@ -0,0 +1,44 @@
+<?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.
+-->
+
+<!--
+This is an optimized layout for a screen, with the minimum set of features
+enabled.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:fitsSystemWindows="true">
+ <TextView android:id="@+id/title"
+ style="?android:attr/windowTitleStyle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:paddingTop="16dip" />
+ <FrameLayout
+ android:layout_width="match_parent" android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:foreground="?android:attr/windowContentOverlay">
+ <FrameLayout android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </FrameLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/dialog_title_quantum.xml b/core/res/res/layout/dialog_title_quantum.xml
deleted file mode 100644
index b92c1e7..0000000
--- a/core/res/res/layout/dialog_title_quantum.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?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.
--->
-
-<!--
-This is an optimized layout for a screen, with the minimum set of features
-enabled.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:fitsSystemWindows="true">
- <TextView android:id="@+id/alertTitle"
- style="?android:attr/windowTitleStyle"
- android:singleLine="true"
- android:ellipsize="end"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="viewStart"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="16dip" />
- <FrameLayout
- android:layout_width="match_parent" android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:foreground="?android:attr/windowContentOverlay">
- <FrameLayout android:id="@android:id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </FrameLayout>
-</LinearLayout>
diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
new file mode 100644
index 0000000..7ccaad5
--- /dev/null
+++ b/core/res/res/layout/notification_material_action.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ android:id="@+id/action0"
+ android:layout_width="0dp"
+ android:layout_height="48dp"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:drawablePadding="8dp"
+ android:paddingStart="8dp"
+ android:textColor="#555555"
+ android:textSize="14dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ />
diff --git a/core/res/res/layout/notification_quantum_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
similarity index 100%
rename from core/res/res/layout/notification_quantum_action_list.xml
rename to core/res/res/layout/notification_material_action_list.xml
diff --git a/core/res/res/layout/notification_material_action_tombstone.xml b/core/res/res/layout/notification_material_action_tombstone.xml
new file mode 100644
index 0000000..8bf456e
--- /dev/null
+++ b/core/res/res/layout/notification_material_action_tombstone.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ android:id="@+id/action0"
+ android:layout_width="0dp"
+ android:layout_height="48dp"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:drawablePadding="8dp"
+ android:paddingStart="8dp"
+ android:textColor="#555555"
+ android:textSize="14dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:alpha="0.5"
+ android:enabled="false"
+ />
diff --git a/core/res/res/layout/notification_material_media_action.xml b/core/res/res/layout/notification_material_media_action.xml
new file mode 100644
index 0000000..331ee57
--- /dev/null
+++ b/core/res/res/layout/notification_material_media_action.xml
@@ -0,0 +1,25 @@
+<?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
+ -->
+
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.Light.Button.Borderless.Small"
+ android:id="@+id/action0"
+ android:layout_width="60dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center"
+ />
diff --git a/core/res/res/layout/notification_quantum_action.xml b/core/res/res/layout/notification_quantum_action.xml
deleted file mode 100644
index 0986343..0000000
--- a/core/res/res/layout/notification_quantum_action.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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
- -->
-
-<Button xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Quantum.Light.Button.Borderless.Small"
- android:id="@+id/action0"
- android:layout_width="0dp"
- android:layout_height="48dp"
- android:layout_weight="1"
- android:gravity="start|center_vertical"
- android:drawablePadding="8dp"
- android:paddingStart="8dp"
- android:textColor="#555555"
- android:textSize="14dp"
- android:singleLine="true"
- android:ellipsize="end"
- />
diff --git a/core/res/res/layout/notification_quantum_action_tombstone.xml b/core/res/res/layout/notification_quantum_action_tombstone.xml
deleted file mode 100644
index 51e4205..0000000
--- a/core/res/res/layout/notification_quantum_action_tombstone.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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
- -->
-
-<Button xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Quantum.Light.Button.Borderless.Small"
- android:id="@+id/action0"
- android:layout_width="0dp"
- android:layout_height="48dp"
- android:layout_weight="1"
- android:gravity="start|center_vertical"
- android:drawablePadding="8dp"
- android:paddingStart="8dp"
- android:textColor="#555555"
- android:textSize="14dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:alpha="0.5"
- android:enabled="false"
- />
diff --git a/core/res/res/layout/notification_quantum_media_action.xml b/core/res/res/layout/notification_quantum_media_action.xml
deleted file mode 100644
index 17f0848..0000000
--- a/core/res/res/layout/notification_quantum_media_action.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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
- -->
-
-<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
- style="@android:style/Widget.Quantum.Light.Button.Borderless.Small"
- android:id="@+id/action0"
- android:layout_width="60dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center"
- />
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
new file mode 100644
index 0000000..ab13b98
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ internal:layout_minHeight="64dp"
+ internal:layout_maxHeight="64dp"
+ >
+ <include layout="@layout/notification_template_icon_group"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+
+ android:layout_gravity="fill_vertical"
+ android:layout_marginStart="@dimen/notification_large_icon_width"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:orientation="vertical"
+ android:paddingEnd="8dp"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp"
+ android:gravity="top"
+ >
+ <LinearLayout
+ android:id="@+id/line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="6dp"
+ android:layout_marginStart="8dp"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:layout_weight="1"
+ />
+ <ViewStub android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_time"
+ />
+ <ViewStub android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_chronometer"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dp"
+ android:layout_marginBottom="-2dp"
+ android:layout_marginStart="8dp"
+ android:singleLine="true"
+ android:fadingEdge="horizontal"
+ android:ellipsize="marquee"
+ android:visibility="gone"
+ />
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="12dp"
+ android:layout_marginStart="8dp"
+ android:visibility="gone"
+ style="@style/Widget.StatusBar.Material.ProgressBar"
+ />
+ <LinearLayout
+ android:id="@+id/line3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_marginStart="8dp"
+ >
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingStart="8dp"
+ />
+ <ImageView android:id="@+id/profile_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:layout_marginStart="8dp"
+ android:scaleType="centerInside"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
new file mode 100644
index 0000000..0564a8f
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ internal:layout_minHeight="65dp"
+ internal:layout_maxHeight="unbounded"
+ >
+ <include layout="@layout/notification_template_icon_group"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_vertical"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:orientation="vertical"
+ android:gravity="top"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_large_icon_width"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:paddingTop="2dp"
+ android:orientation="vertical"
+ >
+ <LinearLayout
+ android:id="@+id/line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="6dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginStart="8dp"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:layout_weight="1"
+ />
+ <ViewStub android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_time"
+ />
+ <ViewStub android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_chronometer"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dp"
+ android:layout_marginBottom="-2dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:fadingEdge="horizontal"
+ android:ellipsize="marquee"
+ android:visibility="gone"
+ />
+ <TextView android:id="@+id/big_text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="false"
+ android:visibility="gone"
+ />
+ <LinearLayout
+ android:id="@+id/line3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ >
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingStart="8dp"
+ />
+ <ImageView android:id="@+id/profile_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:layout_marginStart="8dp"
+ android:scaleType="centerInside"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="12dp"
+ android:layout_marginBottom="8dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:visibility="gone"
+ style="@style/Widget.StatusBar.Material.ProgressBar"
+ />
+ </LinearLayout>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:id="@+id/action_divider"
+ android:visibility="gone"
+ android:background="@drawable/list_divider_holo_light" />
+ <include
+ layout="@layout/notification_material_action_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_large_icon_width"
+ />
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
new file mode 100644
index 0000000..f8e1986
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ internal:layout_minHeight="65dp"
+ internal:layout_maxHeight="unbounded"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_vertical"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:orientation="vertical"
+ android:gravity="top"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/notification_large_icon_width"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:paddingTop="2dp"
+ android:orientation="vertical"
+ >
+ <LinearLayout
+ android:id="@+id/line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="6dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginStart="8dp"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:layout_weight="1"
+ />
+ <ViewStub android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_time"
+ />
+ <ViewStub android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_chronometer"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dp"
+ android:layout_marginBottom="-2dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:fadingEdge="horizontal"
+ android:ellipsize="marquee"
+ android:visibility="gone"
+ />
+ <TextView android:id="@+id/big_text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="false"
+ android:visibility="gone"
+ />
+ <LinearLayout
+ android:id="@+id/line3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ >
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingStart="8dp"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:id="@+id/media_action_area"
+ android:background="@color/notification_media_action_bg"
+ >
+ <LinearLayout
+ android:id="@+id/actions"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="6dp"
+ android:orientation="horizontal"
+ >
+ <!-- media buttons will be added here -->
+ </LinearLayout>
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="6dp"
+ android:layout_gravity="top"
+ android:visibility="gone"
+ style="@style/Widget.StatusBar.Material.ProgressBar.Media"
+ />
+ </FrameLayout>
+ </LinearLayout>
+ <include layout="@layout/notification_template_icon_group"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ />
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
new file mode 100644
index 0000000..74819fd
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ internal:layout_minHeight="65dp"
+ internal:layout_maxHeight="unbounded"
+ >
+ <ImageView
+ android:id="@+id/big_picture"
+ android:layout_width="match_parent"
+ android:layout_height="192dp"
+ android:layout_marginTop="64dp"
+ android:layout_gravity="bottom"
+ android:scaleType="centerCrop"
+ />
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="6dp"
+ android:layout_marginTop="64dp"
+ android:scaleType="fitXY"
+ android:src="@drawable/title_bar_shadow"
+ />
+ <include layout="@layout/notification_template_material_base"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="208dp"
+ android:paddingStart="64dp"
+ android:layout_gravity="bottom"
+ android:background="#CCEEEEEE"
+ >
+ <include
+ layout="@layout/notification_material_action_list"
+ android:id="@+id/actions"
+ android:layout_gravity="bottom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+ </FrameLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
new file mode 100644
index 0000000..1de5add
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ internal:layout_minHeight="65dp"
+ internal:layout_maxHeight="unbounded"
+ >
+ <include layout="@layout/notification_template_icon_group"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_vertical"
+ android:layout_marginStart="@dimen/notification_large_icon_width"
+ android:orientation="vertical"
+ android:paddingTop="0dp"
+ android:paddingBottom="2dp"
+ android:gravity="top"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:orientation="vertical"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_weight="1"
+ >
+ <LinearLayout
+ android:id="@+id/line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:orientation="horizontal"
+ android:layout_gravity="top"
+ android:layout_weight="0"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:layout_weight="1"
+ />
+ <ViewStub android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_time"
+ />
+ <ViewStub android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_chronometer"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dp"
+ android:layout_marginBottom="-2dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:fadingEdge="horizontal"
+ android:ellipsize="marquee"
+ android:layout_weight="0"
+ android:visibility="gone"
+ />
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="12dp"
+ android:layout_marginBottom="8dp"
+ android:layout_marginEnd="8dp"
+ android:visibility="gone"
+ android:layout_weight="0"
+ style="@style/Widget.StatusBar.Material.ProgressBar"
+ />
+ <TextView android:id="@+id/big_text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginBottom="10dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="false"
+ android:visibility="gone"
+ android:maxLines="8"
+ android:ellipsize="end"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:id="@+id/action_divider"
+ android:visibility="gone"
+ android:background="@drawable/list_divider_holo_light" />
+ <include
+ layout="@layout/notification_material_action_list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:id="@+id/overflow_divider"
+ android:layout_marginBottom="8dp"
+ android:visibility="visible"
+ android:background="@drawable/list_divider_holo_light" />
+ <LinearLayout
+ android:id="@+id/line3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginBottom="8dp"
+ android:layout_marginEnd="8dp"
+ android:orientation="horizontal"
+ android:layout_weight="0"
+ android:gravity="center_vertical"
+ >
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingStart="8dp"
+ />
+ <ImageView android:id="@+id/profile_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:layout_marginStart="8dp"
+ android:scaleType="centerInside"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
new file mode 100644
index 0000000..8411ff5
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ internal:layout_minHeight="65dp"
+ internal:layout_maxHeight="unbounded"
+ >
+ <include layout="@layout/notification_template_icon_group"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_vertical"
+ android:layout_marginStart="@dimen/notification_large_icon_width"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:orientation="vertical"
+ android:paddingTop="0dp"
+ android:paddingBottom="2dp"
+ android:gravity="top"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:paddingTop="2dp"
+ android:orientation="vertical"
+ >
+ <LinearLayout
+ android:id="@+id/line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:paddingTop="6dp"
+ android:orientation="horizontal"
+ android:layout_weight="0"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:layout_weight="1"
+ />
+ <ViewStub android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_time"
+ />
+ <ViewStub android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_chronometer"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dp"
+ android:layout_marginBottom="-2dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:fadingEdge="horizontal"
+ android:ellipsize="marquee"
+ android:visibility="gone"
+ android:layout_weight="0"
+ />
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="12dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:visibility="gone"
+ android:layout_weight="0"
+ style="@style/Widget.Material.Light.ProgressBar.Horizontal"
+ />
+ <TextView android:id="@+id/inbox_text0"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text1"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text3"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text4"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text5"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text6"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_more"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ android:text="@android:string/ellipsis"
+ />
+ <FrameLayout
+ android:id="@+id/inbox_end_pad"
+ android:layout_width="match_parent"
+ android:layout_height="8dip"
+ android:visibility="gone"
+ android:layout_weight="0"
+ />
+ </LinearLayout>
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:id="@+id/action_divider"
+ android:visibility="gone"
+ android:background="@drawable/list_divider_holo_light" />
+ <include
+ layout="@layout/notification_material_action_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ />
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:id="@+id/overflow_divider"
+ android:visibility="visible"
+ android:background="@drawable/list_divider_holo_light" />
+ <LinearLayout
+ android:id="@+id/line3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginBottom="8dp"
+ android:layout_marginEnd="8dp"
+ android:orientation="horizontal"
+ android:layout_weight="0"
+ android:gravity="center_vertical"
+ >
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingStart="8dp"
+ />
+ <ImageView android:id="@+id/profile_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:layout_marginStart="8dp"
+ android:scaleType="centerInside"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
new file mode 100644
index 0000000..c2fc006
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -0,0 +1,135 @@
+<?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
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:internal="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:orientation="horizontal"
+ internal:layout_minHeight="64dp"
+ internal:layout_maxHeight="64dp"
+ >
+ <include layout="@layout/notification_template_icon_group"
+ android:layout_width="@dimen/notification_large_icon_width"
+ android:layout_height="@dimen/notification_large_icon_height"
+ android:layout_weight="0"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="fill_vertical"
+ android:minHeight="@dimen/notification_large_icon_height"
+ android:orientation="vertical"
+ android:paddingEnd="8dp"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp"
+ android:gravity="top"
+ >
+ <LinearLayout
+ android:id="@+id/line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="6dp"
+ android:layout_marginStart="8dp"
+ android:orientation="horizontal"
+ >
+ <TextView android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:layout_weight="1"
+ />
+ <ViewStub android:id="@+id/time"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_time"
+ />
+ <ViewStub android:id="@+id/chronometer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:visibility="gone"
+ android:layout="@layout/notification_template_part_chronometer"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/text2"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Line2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dp"
+ android:layout_marginBottom="-2dp"
+ android:layout_marginStart="8dp"
+ android:singleLine="true"
+ android:fadingEdge="horizontal"
+ android:ellipsize="marquee"
+ android:visibility="gone"
+ />
+ <ProgressBar
+ android:id="@android:id/progress"
+ android:layout_width="match_parent"
+ android:layout_height="12dp"
+ android:layout_marginStart="8dp"
+ android:visibility="gone"
+ style="@style/Widget.StatusBar.Material.ProgressBar"
+ />
+ <LinearLayout
+ android:id="@+id/line3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:layout_marginStart="8dp"
+ >
+ <TextView android:id="@+id/text"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ />
+ <TextView android:id="@+id/info"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:gravity="center"
+ android:paddingStart="8dp"
+ />
+ </LinearLayout>
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/actions"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|end"
+ android:orientation="horizontal"
+ >
+ <!-- media buttons will be added here -->
+ </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/notification_template_part_chronometer.xml b/core/res/res/layout/notification_template_part_chronometer.xml
index 9b6e6c5..87dfe1f 100644
--- a/core/res/res/layout/notification_template_part_chronometer.xml
+++ b/core/res/res/layout/notification_template_part_chronometer.xml
@@ -15,7 +15,7 @@
-->
<Chronometer android:id="@+id/chronometer" xmlns:android="http://schemas.android.com/apk/res/android"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Time"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/layout/notification_template_part_time.xml b/core/res/res/layout/notification_template_part_time.xml
index b559dce..5982c48 100644
--- a/core/res/res/layout/notification_template_part_time.xml
+++ b/core/res/res/layout/notification_template_part_time.xml
@@ -15,7 +15,7 @@
-->
<DateTimeView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Time"
+ android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent.Time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/core/res/res/layout/notification_template_quantum_base.xml b/core/res/res/layout/notification_template_quantum_base.xml
deleted file mode 100644
index 789bf32..0000000
--- a/core/res/res/layout/notification_template_quantum_base.xml
+++ /dev/null
@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- >
- <include layout="@layout/notification_template_icon_group"
- android:layout_width="@dimen/notification_large_icon_width"
- android:layout_height="@dimen/notification_large_icon_height"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
-
- android:layout_gravity="fill_vertical"
- android:layout_marginStart="@dimen/notification_large_icon_width"
- android:minHeight="@dimen/notification_large_icon_height"
- android:orientation="vertical"
- android:paddingEnd="8dp"
- android:paddingTop="2dp"
- android:paddingBottom="2dp"
- android:gravity="top"
- >
- <LinearLayout
- android:id="@+id/line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="6dp"
- android:layout_marginStart="8dp"
- android:orientation="horizontal"
- >
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:layout_weight="1"
- />
- <ViewStub android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_time"
- />
- <ViewStub android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_chronometer"
- />
- </LinearLayout>
- <TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Line2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dp"
- android:layout_marginBottom="-2dp"
- android:layout_marginStart="8dp"
- android:singleLine="true"
- android:fadingEdge="horizontal"
- android:ellipsize="marquee"
- android:visibility="gone"
- />
- <ProgressBar
- android:id="@android:id/progress"
- android:layout_width="match_parent"
- android:layout_height="12dp"
- android:layout_marginStart="8dp"
- android:visibility="gone"
- style="@style/Widget.StatusBar.Quantum.ProgressBar"
- />
- <LinearLayout
- android:id="@+id/line3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:layout_marginStart="8dp"
- >
- <TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- />
- <TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:singleLine="true"
- android:gravity="center"
- android:paddingStart="8dp"
- />
- </LinearLayout>
- </LinearLayout>
-</FrameLayout>
diff --git a/core/res/res/layout/notification_template_quantum_big_base.xml b/core/res/res/layout/notification_template_quantum_big_base.xml
deleted file mode 100644
index 8cb5549..0000000
--- a/core/res/res/layout/notification_template_quantum_big_base.xml
+++ /dev/null
@@ -1,155 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
- >
- <include layout="@layout/notification_template_icon_group"
- android:layout_width="@dimen/notification_large_icon_width"
- android:layout_height="@dimen/notification_large_icon_height"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_vertical"
- android:minHeight="@dimen/notification_large_icon_height"
- android:orientation="vertical"
- android:gravity="top"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_large_icon_width"
- android:minHeight="@dimen/notification_large_icon_height"
- android:paddingTop="2dp"
- android:orientation="vertical"
- >
- <LinearLayout
- android:id="@+id/line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="6dp"
- android:layout_marginEnd="8dp"
- android:layout_marginStart="8dp"
- android:orientation="horizontal"
- >
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:layout_weight="1"
- />
- <ViewStub android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_time"
- />
- <ViewStub android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_chronometer"
- />
- </LinearLayout>
- <TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Line2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dp"
- android:layout_marginBottom="-2dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:fadingEdge="horizontal"
- android:ellipsize="marquee"
- android:visibility="gone"
- />
- <TextView android:id="@+id/big_text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="false"
- android:visibility="gone"
- />
- <LinearLayout
- android:id="@+id/line3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- >
- <TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- />
- <TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:singleLine="true"
- android:gravity="center"
- android:paddingStart="8dp"
- />
- </LinearLayout>
- <ProgressBar
- android:id="@android:id/progress"
- android:layout_width="match_parent"
- android:layout_height="12dp"
- android:layout_marginBottom="8dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:visibility="gone"
- style="@style/Widget.Quantum.Light.ProgressBar.Horizontal"
- />
- </LinearLayout>
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:id="@+id/action_divider"
- android:visibility="gone"
- android:background="@drawable/list_divider_holo_light" />
- <include
- layout="@layout/notification_quantum_action_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/notification_large_icon_width"
- />
- </LinearLayout>
-</FrameLayout>
diff --git a/core/res/res/layout/notification_template_quantum_big_media.xml b/core/res/res/layout/notification_template_quantum_big_media.xml
deleted file mode 100644
index 5c9334e..0000000
--- a/core/res/res/layout/notification_template_quantum_big_media.xml
+++ /dev/null
@@ -1,158 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_vertical"
- android:minHeight="@dimen/notification_large_icon_height"
- android:orientation="vertical"
- android:gravity="top"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="@dimen/notification_large_icon_width"
- android:minHeight="@dimen/notification_large_icon_height"
- android:paddingTop="2dp"
- android:orientation="vertical"
- android:background="@color/notification_media_info_bg"
- >
- <LinearLayout
- android:id="@+id/line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="6dp"
- android:layout_marginEnd="8dp"
- android:layout_marginStart="8dp"
- android:orientation="horizontal"
- >
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:layout_weight="1"
- />
- <ViewStub android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_time"
- />
- <ViewStub android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_chronometer"
- />
- </LinearLayout>
- <TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Line2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dp"
- android:layout_marginBottom="-2dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:fadingEdge="horizontal"
- android:ellipsize="marquee"
- android:visibility="gone"
- />
- <TextView android:id="@+id/big_text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="false"
- android:visibility="gone"
- />
- <LinearLayout
- android:id="@+id/line3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- >
- <TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- />
- <TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:singleLine="true"
- android:gravity="center"
- android:paddingStart="8dp"
- />
- </LinearLayout>
- </LinearLayout>
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:id="@+id/media_action_area"
- android:background="@color/notification_media_action_bg"
- >
- <LinearLayout
- android:id="@+id/actions"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="6dp"
- android:orientation="horizontal"
- >
- <!-- media buttons will be added here -->
- </LinearLayout>
- <ProgressBar
- android:id="@android:id/progress"
- android:layout_width="match_parent"
- android:layout_height="6dp"
- android:layout_gravity="top"
- android:visibility="gone"
- style="@style/Widget.StatusBar.Quantum.ProgressBar"
- />
- </FrameLayout>
- </LinearLayout>
- <include layout="@layout/notification_template_icon_group"
- android:layout_width="@dimen/notification_large_icon_width"
- android:layout_height="@dimen/notification_large_icon_height"
- />
-</FrameLayout>
diff --git a/core/res/res/layout/notification_template_quantum_big_picture.xml b/core/res/res/layout/notification_template_quantum_big_picture.xml
deleted file mode 100644
index f68e414..0000000
--- a/core/res/res/layout/notification_template_quantum_big_picture.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
- >
- <ImageView
- android:id="@+id/big_picture"
- android:layout_width="match_parent"
- android:layout_height="192dp"
- android:layout_marginTop="64dp"
- android:layout_gravity="bottom"
- android:scaleType="centerCrop"
- />
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="6dp"
- android:layout_marginTop="64dp"
- android:scaleType="fitXY"
- android:src="@drawable/title_bar_shadow"
- />
- <include layout="@layout/notification_template_quantum_base"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="208dp"
- android:paddingStart="64dp"
- android:layout_gravity="bottom"
- android:background="#CCEEEEEE"
- >
- <include
- layout="@layout/notification_quantum_action_list"
- android:id="@+id/actions"
- android:layout_gravity="bottom"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
- </FrameLayout>
-</FrameLayout>
diff --git a/core/res/res/layout/notification_template_quantum_big_text.xml b/core/res/res/layout/notification_template_quantum_big_text.xml
deleted file mode 100644
index bbd1071..0000000
--- a/core/res/res/layout/notification_template_quantum_big_text.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
- >
- <include layout="@layout/notification_template_icon_group"
- android:layout_width="@dimen/notification_large_icon_width"
- android:layout_height="@dimen/notification_large_icon_height"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_vertical"
- android:layout_marginStart="@dimen/notification_large_icon_width"
- android:orientation="vertical"
- android:paddingTop="0dp"
- android:paddingBottom="2dp"
- android:gravity="top"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_large_icon_height"
- android:orientation="vertical"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:layout_weight="1"
- >
- <LinearLayout
- android:id="@+id/line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:orientation="horizontal"
- android:layout_gravity="top"
- android:layout_weight="0"
- >
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:layout_weight="1"
- />
- <ViewStub android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_time"
- />
- <ViewStub android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_chronometer"
- />
- </LinearLayout>
- <TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Line2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dp"
- android:layout_marginBottom="-2dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:fadingEdge="horizontal"
- android:ellipsize="marquee"
- android:layout_weight="0"
- android:visibility="gone"
- />
- <ProgressBar
- android:id="@android:id/progress"
- android:layout_width="match_parent"
- android:layout_height="12dp"
- android:layout_marginBottom="8dp"
- android:layout_marginEnd="8dp"
- android:visibility="gone"
- android:layout_weight="0"
- style="@style/Widget.Quantum.Light.ProgressBar.Horizontal"
- />
- <TextView android:id="@+id/big_text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginBottom="10dp"
- android:layout_marginEnd="8dp"
- android:singleLine="false"
- android:visibility="gone"
- android:maxLines="8"
- android:ellipsize="end"
- android:layout_weight="1"
- />
- </LinearLayout>
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:id="@+id/action_divider"
- android:visibility="gone"
- android:background="@drawable/list_divider_holo_light" />
- <include
- layout="@layout/notification_quantum_action_list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:id="@+id/overflow_divider"
- android:layout_marginBottom="8dp"
- android:visibility="visible"
- android:background="@drawable/list_divider_holo_light" />
- <LinearLayout
- android:id="@+id/line3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginBottom="8dp"
- android:layout_marginEnd="8dp"
- android:orientation="horizontal"
- android:layout_weight="0"
- android:gravity="center_vertical"
- >
- <TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- />
- <TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:singleLine="true"
- android:gravity="center"
- android:paddingStart="8dp"
- />
- </LinearLayout>
- </LinearLayout>
-</FrameLayout>
diff --git a/core/res/res/layout/notification_template_quantum_inbox.xml b/core/res/res/layout/notification_template_quantum_inbox.xml
deleted file mode 100644
index a071d59..0000000
--- a/core/res/res/layout/notification_template_quantum_inbox.xml
+++ /dev/null
@@ -1,254 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
- >
- <include layout="@layout/notification_template_icon_group"
- android:layout_width="@dimen/notification_large_icon_width"
- android:layout_height="@dimen/notification_large_icon_height"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_vertical"
- android:layout_marginStart="@dimen/notification_large_icon_width"
- android:minHeight="@dimen/notification_large_icon_height"
- android:orientation="vertical"
- android:paddingTop="0dp"
- android:paddingBottom="2dp"
- android:gravity="top"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_large_icon_height"
- android:paddingTop="2dp"
- android:orientation="vertical"
- >
- <LinearLayout
- android:id="@+id/line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:paddingTop="6dp"
- android:orientation="horizontal"
- android:layout_weight="0"
- >
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:layout_weight="1"
- />
- <ViewStub android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_time"
- />
- <ViewStub android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_chronometer"
- />
- </LinearLayout>
- <TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Line2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dp"
- android:layout_marginBottom="-2dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:fadingEdge="horizontal"
- android:ellipsize="marquee"
- android:visibility="gone"
- android:layout_weight="0"
- />
- <ProgressBar
- android:id="@android:id/progress"
- android:layout_width="match_parent"
- android:layout_height="12dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:visibility="gone"
- android:layout_weight="0"
- style="@style/Widget.Quantum.Light.ProgressBar.Horizontal"
- />
- <TextView android:id="@+id/inbox_text0"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text1"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text3"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text4"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginStart="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text5"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_text6"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- />
- <TextView android:id="@+id/inbox_more"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:singleLine="true"
- android:ellipsize="end"
- android:visibility="gone"
- android:layout_weight="1"
- android:text="@android:string/ellipsis"
- />
- <FrameLayout
- android:id="@+id/inbox_end_pad"
- android:layout_width="match_parent"
- android:layout_height="8dip"
- android:visibility="gone"
- android:layout_weight="0"
- />
- </LinearLayout>
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:id="@+id/action_divider"
- android:visibility="gone"
- android:background="@drawable/list_divider_holo_light" />
- <include
- layout="@layout/notification_quantum_action_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- />
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:id="@+id/overflow_divider"
- android:visibility="visible"
- android:background="@drawable/list_divider_holo_light" />
- <LinearLayout
- android:id="@+id/line3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginStart="8dp"
- android:layout_marginBottom="8dp"
- android:layout_marginEnd="8dp"
- android:orientation="horizontal"
- android:layout_weight="0"
- android:gravity="center_vertical"
- >
- <TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- />
- <TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:singleLine="true"
- android:gravity="center"
- android:paddingStart="8dp"
- />
- </LinearLayout>
- </LinearLayout>
-</FrameLayout>
diff --git a/core/res/res/layout/notification_template_quantum_media.xml b/core/res/res/layout/notification_template_quantum_media.xml
deleted file mode 100644
index 14fabce..0000000
--- a/core/res/res/layout/notification_template_quantum_media.xml
+++ /dev/null
@@ -1,135 +0,0 @@
-<?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
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/status_bar_latest_event_content"
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:orientation="horizontal"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- >
- <include layout="@layout/notification_template_icon_group"
- android:layout_width="@dimen/notification_large_icon_width"
- android:layout_height="@dimen/notification_large_icon_height"
- android:layout_weight="0"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="fill_vertical"
- android:minHeight="@dimen/notification_large_icon_height"
- android:orientation="vertical"
- android:paddingEnd="8dp"
- android:paddingTop="2dp"
- android:paddingBottom="2dp"
- android:gravity="top"
- >
- <LinearLayout
- android:id="@+id/line1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="6dp"
- android:layout_marginStart="8dp"
- android:orientation="horizontal"
- >
- <TextView android:id="@+id/title"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- android:layout_weight="1"
- />
- <ViewStub android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_time"
- />
- <ViewStub android:id="@+id/chronometer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:visibility="gone"
- android:layout="@layout/notification_template_part_chronometer"
- />
- </LinearLayout>
- <TextView android:id="@+id/text2"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Line2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dp"
- android:layout_marginBottom="-2dp"
- android:layout_marginStart="8dp"
- android:singleLine="true"
- android:fadingEdge="horizontal"
- android:ellipsize="marquee"
- android:visibility="gone"
- />
- <ProgressBar
- android:id="@android:id/progress"
- android:layout_width="match_parent"
- android:layout_height="12dp"
- android:layout_marginStart="8dp"
- android:visibility="gone"
- style="@style/Widget.StatusBar.Quantum.ProgressBar"
- />
- <LinearLayout
- android:id="@+id/line3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:layout_marginStart="8dp"
- >
- <TextView android:id="@+id/text"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"
- />
- <TextView android:id="@+id/info"
- android:textAppearance="@style/TextAppearance.StatusBar.Quantum.EventContent.Info"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="0"
- android:singleLine="true"
- android:gravity="center"
- android:paddingStart="8dp"
- />
- </LinearLayout>
- </LinearLayout>
- <LinearLayout
- android:id="@+id/actions"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical|end"
- android:orientation="horizontal"
- >
- <!-- media buttons will be added here -->
- </LinearLayout>
-</LinearLayout>
diff --git a/core/res/res/layout/preference_category_material.xml b/core/res/res/layout/preference_category_material.xml
new file mode 100644
index 0000000..456b252
--- /dev/null
+++ b/core/res/res/layout/preference_category_material.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<!-- Layout used for PreferenceCategory in a PreferenceActivity. -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+android:id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dip"
+ android:textAppearance="@style/TextAppearance.Material.Body2"
+ android:textColor="?android:attr/colorAccent"
+ android:paddingStart="?attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+ android:paddingTop="16dip" />
diff --git a/core/res/res/layout/preference_category_quantum.xml b/core/res/res/layout/preference_category_quantum.xml
deleted file mode 100644
index 032e09d..0000000
--- a/core/res/res/layout/preference_category_quantum.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
--->
-
-<!-- Layout used for PreferenceCategory in a PreferenceActivity. -->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+android:id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="16dip"
- android:textAppearance="@style/TextAppearance.Quantum.Body2"
- android:textColor="?android:attr/textColorSecondary"
- android:textStyle="bold"
- android:paddingStart="?attr/listPreferredItemPaddingStart"
- android:paddingEnd="?attr/listPreferredItemPaddingEnd"
- android:paddingTop="16dip" />
diff --git a/core/res/res/layout/preference_child_quantum.xml b/core/res/res/layout/preference_child_material.xml
similarity index 100%
rename from core/res/res/layout/preference_child_quantum.xml
rename to core/res/res/layout/preference_child_material.xml
diff --git a/core/res/res/layout/preference_information_quantum.xml b/core/res/res/layout/preference_information_material.xml
similarity index 100%
rename from core/res/res/layout/preference_information_quantum.xml
rename to core/res/res/layout/preference_information_material.xml
diff --git a/core/res/res/layout/preference_material.xml b/core/res/res/layout/preference_material.xml
new file mode 100644
index 0000000..a9599139
--- /dev/null
+++ b/core/res/res/layout/preference_material.xml
@@ -0,0 +1,77 @@
+<?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.
+-->
+
+<!-- Layout for a Preference in a PreferenceActivity. The
+ Preference is able to place a specific widget for its particular
+ type in the "widget_frame" layout. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/activatedBackgroundIndicator">
+
+ <LinearLayout
+ android:id="@+android:id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="58dip"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:id="@+android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleType="centerInside"
+ android:layout_marginEnd="8dip" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dip"
+ android:paddingBottom="16dip">
+
+ <TextView android:id="@+android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView android:id="@+android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout android:id="@+android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="58dip"
+ android:gravity="right|center_vertical"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/preference_quantum.xml b/core/res/res/layout/preference_quantum.xml
deleted file mode 100644
index a4fe73d..0000000
--- a/core/res/res/layout/preference_quantum.xml
+++ /dev/null
@@ -1,77 +0,0 @@
-<?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.
--->
-
-<!-- Layout for a Preference in a PreferenceActivity. The
- Preference is able to place a specific widget for its particular
- type in the "widget_frame" layout. -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:gravity="center_vertical"
- android:paddingStart="?attr/listPreferredItemPaddingStart"
- android:paddingEnd="?attr/listPreferredItemPaddingEnd"
- android:background="?android:attr/activatedBackgroundIndicator">
-
- <LinearLayout
- android:id="@+android:id/icon_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="58dip"
- android:gravity="left|center_vertical"
- android:orientation="horizontal">
- <ImageView
- android:id="@+android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scaleType="centerInside"
- android:layout_marginEnd="8dip" />
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="16dip"
- android:paddingBottom="16dip">
-
- <TextView android:id="@+android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceListItem"
- android:ellipsize="marquee" />
-
- <TextView android:id="@+android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_alignStart="@android:id/title"
- android:textAppearance="?android:attr/textAppearanceListItemSecondary"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="10" />
-
- </RelativeLayout>
-
- <!-- Preference should place its actual preference widget here. -->
- <LinearLayout android:id="@+android:id/widget_frame"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:minWidth="58dip"
- android:gravity="right|center_vertical"
- android:orientation="vertical" />
-
-</LinearLayout>
diff --git a/core/res/res/layout/preference_widget_checkbox.xml b/core/res/res/layout/preference_widget_checkbox.xml
index bfd7f0a..f794346 100644
--- a/core/res/res/layout/preference_widget_checkbox.xml
+++ b/core/res/res/layout/preference_widget_checkbox.xml
@@ -21,4 +21,5 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
- android:clickable="false" />
+ android:clickable="false"
+ android:background="@null" />
diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml
index 534c7ec..04a310b 100644
--- a/core/res/res/layout/preference_widget_switch.xml
+++ b/core/res/res/layout/preference_widget_switch.xml
@@ -21,4 +21,5 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dip"
- android:focusable="false" />
+ android:focusable="false"
+ android:background="@null" />
diff --git a/core/res/res/layout/progress_dialog_quantum.xml b/core/res/res/layout/progress_dialog_material.xml
similarity index 100%
rename from core/res/res/layout/progress_dialog_quantum.xml
rename to core/res/res/layout/progress_dialog_material.xml
diff --git a/core/res/res/layout/select_dialog_item_quantum.xml b/core/res/res/layout/select_dialog_item_material.xml
similarity index 100%
rename from core/res/res/layout/select_dialog_item_quantum.xml
rename to core/res/res/layout/select_dialog_item_material.xml
diff --git a/core/res/res/layout/select_dialog_quantum.xml b/core/res/res/layout/select_dialog_material.xml
similarity index 100%
rename from core/res/res/layout/select_dialog_quantum.xml
rename to core/res/res/layout/select_dialog_material.xml
diff --git a/core/res/res/layout/select_dialog_multichoice_quantum.xml b/core/res/res/layout/select_dialog_multichoice_material.xml
similarity index 100%
rename from core/res/res/layout/select_dialog_multichoice_quantum.xml
rename to core/res/res/layout/select_dialog_multichoice_material.xml
diff --git a/core/res/res/layout/select_dialog_singlechoice_quantum.xml b/core/res/res/layout/select_dialog_singlechoice_material.xml
similarity index 100%
rename from core/res/res/layout/select_dialog_singlechoice_quantum.xml
rename to core/res/res/layout/select_dialog_singlechoice_material.xml
diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index dc78174..9444164 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -22,7 +22,7 @@
android:layout_height="wrap_content"
android:background="#FFFF00FF"
>
- <include layout="@layout/notification_template_quantum_base"
+ <include layout="@layout/notification_template_material_base"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
diff --git a/core/res/res/layout/tab_indicator_material.xml b/core/res/res/layout/tab_indicator_material.xml
new file mode 100644
index 0000000..7dba219
--- /dev/null
+++ b/core/res/res/layout/tab_indicator_material.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="?android:attr/actionBarSize"
+ android:orientation="horizontal"
+ style="@android:style/Widget.Material.Tab">
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ style="@android:style/Widget.Material.TabText" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/tab_indicator_quantum.xml b/core/res/res/layout/tab_indicator_quantum.xml
deleted file mode 100644
index fcb2d5f..0000000
--- a/core/res/res/layout/tab_indicator_quantum.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_height="?android:attr/actionBarSize"
- android:orientation="horizontal"
- style="@android:style/Widget.Quantum.Tab">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:visibility="gone" />
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- style="@android:style/Widget.Quantum.TabText" />
-
-</LinearLayout>
diff --git a/core/res/res/transition/fade.xml b/core/res/res/transition/fade.xml
new file mode 100644
index 0000000..22397d4
--- /dev/null
+++ b/core/res/res/transition/fade.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<fade xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/core/res/res/transition/move.xml b/core/res/res/transition/move.xml
new file mode 100644
index 0000000..d4863ee
--- /dev/null
+++ b/core/res/res/transition/move.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
+ <transitionSet>
+ <changeBounds/>
+ <changeTransform/>
+ <changeClipBounds/>
+ <targets>
+ <target android:excludeClass="android.widget.ImageView"/>
+ </targets>
+ </transitionSet>
+ <moveImage>
+ <targets>
+ <target android:targetClass="android.widget.ImageView"/>
+ </targets>
+ </moveImage>
+</transitionSet>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 21f1401..bd92c53 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Veiligmodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-stelsel"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Persoonlik"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Werk"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Persoonlike programme"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android vir Werk"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Dienste wat jou geld kos"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Doen dinge wat jou geld kan kos."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Jou boodskappe"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Laat die program oudio-uitset vasvang en herlei."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Aktiveerwoord-opsporing"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Laat die program toe om oudio vir Aktiveerwoord-opsporing op te neem. Die opname kan in die agtergrond plaasvind, maar verhoed nie dat ander oudio opgeneem word nie (bv. Kameraopnemer)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Oudio-versending"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Laat die program toe om oudio-versending direk te beheer en oudio-beleidsbesluite ter syde te stel."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"vang video-uitset vas"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Laat die program video-uitset vasvang en herlei."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"vang veilige video-uitset vas"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Laat \'n program toe om keyguard te beheer."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Luister na vertrouenstaatveranderinge."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Laat \'n program toe om vir veranderinge in vertrouenstaat te luister."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Voorsien \'n vertroude agent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Laat \'n program toe om \'n vertroude agent te voorsien."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Verbind met \'n vertrouensagentdiens"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Laat \'n program toe om met \'n vertrouensagentdiens te verbind."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Tree in wisselwerking met opdatering- en terugstellingstelsel"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 0d315a2..b2b93ab 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"የግል"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ስራ"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"የግል መተግበሪያዎች"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android ለስራ"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"ገንዘብ የሚያስወጥዎ አገልግሎቶች"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"ገንዘብ የሚያስወጡህን ነገሮች አድርግ።"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"መልዕክቶችዎ"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"መተግበሪያው የድምጽ ውጽዓት እንዲቀርጽ እና አቅጣጫውን እንዲያዞር ያስችለዋል።"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"ትኩስ ቃል ማወቅ"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"ትኩስ ቃል ለይቶ ለማወቅ ድምጽ እንዲቀርጽ ለመተግበሪያው ይፈቅድለታል። ቀረጻው በጀርባ ሊካሄድ ይችላል ነገር ግን ሌላ የድምጽ ቀረጻዎችን አይከለክልም (ለምሳሌ፣ የካሜራ መቅረጫ)።"</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"የድምጽ ማስተላለፊያ"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"መተግበሪያው በቀጥታ የድምጽ ማስተላለፍን እንዲቆጣጠርና የድምጽ መምሪያ ውሳኔዎችን እንዲጥስ ይፈቅድለታል።"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"የቪዲዮ ውጽዓት ይቅረጹ"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"መተግበሪያው የቪዲዮ ውጽዓት እንዲቀርጽ እና አቅጣጫውን እንዲያዞር ያስችለዋል።"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"ደህንነቱ የተጠበቀ የቪዲዮ ውጽዓት ይቅረጹ"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"አንድ መተግበሪያ የቁልፍ መጠበቂያውን እንዲቆጣጠር ያስችለዋል።"</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"የተአማኒነት ሁኔታ ለውጦችን አዳምጥ።"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"መተግበሪያው በተአማኒነት ሁኔታ ውስጥ ለውጦችን እንዲያዳምጥ ይፈቅዳል።"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"የመታመን ወኪል ያቅርቡ።"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"አንድ መተግበሪያ የመታመን ወኪል እንዲያቀርብ ይፈቅድለታል።"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"ለተአማኒነት ወኪል አገልግሎት ተገዢ አድርግ"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"ለመተግበሪያን የተአማኒነት ወኪል አገልግሎትን እንዲያከብር ይፈቅዳል።"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"ከዝማኔዎች እና ከመልሶ ማግኛ ስርዓቶች ጋር ይገናኙ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index fbdaab4..e5a3f39 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"شخصي"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"عمل"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"التطبيقات الشخصية"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android للعمل"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"الخدمات التي تكلفك المال"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"يمكنك تنفيذ إجراءات يمكن أن تكلفك مالاً."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"رسائلك"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"السماح للتطبيق بالتقاط إخراج الصوت وإعادة توجيهه."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"اكتشاف الكلمة المهمة"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"للسماح للتطبيق بالتقاط الصوت لاكتشاف الكلمة المهمة. يمكن أن يتم الالتقاط في الخلفية ولكنه لا يمنع التقاط الأصوات الأخرى (على سبيل المثال، كاميرا الفيديو)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"التوجيه الصوتي"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"للسماح للتطبيق بالتحكم المباشر في التوجيه الصوتي وتجاوز قرارات السياسات الصوتية."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"التقاط إخراج الفيديو"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"السماح للتطبيق بالتقاط إخراج الفيديو وإعادة توجيهه."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"التقاط إخراج الفيديو الآمن"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"للسماح لأحد التطبيقات بالتحكم في قفل المفاتيح."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"معرفة تغييرات حالة الاعتماد."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"للسماح للتطبيق بالتعرف على التغييرات في حالة الاعتماد."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"توفير وكيل معتمد."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"للسماح لأحد التطبيقات بتوفير وكيل معتمد."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"الالتزام بخدمة الوكيل المعتمد"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"للسماح لأحد التطبيقات بالالتزام بخدمة الوكيل المعتمد."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"التفاعل مع نظام التحديث والاسترداد"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 59bb807..0985844 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Личен"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Служебен"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Лични приложения"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android за работа"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Услуги, които ви струват пари"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Извършват неща, които могат да ви струват пари."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Вашите съобщения"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Разрешава на приложението да записва и пренасочва възпроизвеждания звук."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Откриване на активиращи думи"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Разрешава на приложението да записва звук с цел откриване на активиращи думи. Това може да става на заден план, но не пречи на записването на други звуци (напр. от видеокамерата)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Аудиомаршрутизиране"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Разрешава на приложението директно да контролира аудиомаршрутизирането и да заменя решенията на аудиоправилата."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"записване на възпроизвеждания образ"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Разрешава на приложението да записва и пренасочва възпроизвеждания образ."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"записване на защитеното възпроизвеждане на образ"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Разрешава на приложението да контролира функцията за защита на клавишите."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Следене за промени в състоянието на надеждност"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Разрешава на приложението да следи за промени в състоянието на надеждност."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Предоставяне на trust agent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Разрешава на приложението да предоставя trust agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Обвързване с услуга за trust agents"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Разрешава на приложението да се обвърже с услуга за trust agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Взаимодействие със системата за актуализации и възстановяване"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index a53c131..900aef3 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"+999"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode segur"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Feina"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Aplicacions personals"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android per a la feina"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Serveis de pagament"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Dur a terme activitats de pagament."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Missatges"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Permet que l\'aplicació capturi i redirigeixi la sortida d\'àudio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Detecció de paraules actives"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Permet que l\'aplicació capturi àudio per a la detecció de paraules actives. La captura es pot produir en segon pla però no evita altres captures d\'àudio (per exemple, de càmera de vídeo)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Encaminament d\'àudio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Permet que l\'aplicació controli directament l\'encaminament d\'àudio i sobreescrigui les decisions relacionades amb la política d\'àudio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"captura la sortida de vídeo"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Permet que l\'aplicació capturi i redirigeixi la sortida de vídeo."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"captura la sortida de vídeo segur"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permet que una aplicació controli el bloqueig de les tecles."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Escoltar els canvis de l\'estat de confiança"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permet que una aplicació escolti els canvis en l\'estat de confiança."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Proporcionar un agent de confiança"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permet que una aplicació proporcioni un agent de confiança."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Enllaçar amb el servei d\'un agent de confiança"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permet que una aplicació es vinculi amb el servei d\'un agent de confiança."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interacciona amb el sistema de recuperació i amb les actualitzacions"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index a1e99fa..a004044 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Nouzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osobní"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Práce"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Osobní aplikace"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android pro práci"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Zpoplatněné služby"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Provádět činnosti, které vás mohou stát peníze."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vaše zprávy"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Umožní aplikaci zachytit a přesměrovat výstup zvuku."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Detekce klíčových slov"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Umožní aplikaci zachytávat zvuk za účelem detekce klíčových slov. Zachytávání může probíhat na pozadí a nebrání jinému zaznamenávání zvuku (například videokamerou)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Směrování zvuku"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Umožňuje aplikaci převzít přímou kontrolu nad směrováním zvuku a přepsat nastavené zásady zvuku."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"zachytit výstup videa"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Umožní aplikaci zachytit a přesměrovat výstup videa."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"zachytit zabezpečený výstup videa"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Umožňuje aplikaci ovládat zámek obrazovky."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Naslouchat změnám stavu důvěryhodnosti"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Umožňuje aplikaci naslouchat změnám ve stavu důvěryhodnosti."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Poskytování zástupce důvěryhodnosti"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Umožňuje aplikaci poskytnout zástupce důvěryhodnosti."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Vázat se na službu zástupce důvěryhodnosti"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Umožňuje aplikaci vázat se na službu zástupce důvěryhodnosti."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakce se systémem aktualizací a obnovení"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index b17b8c6..7ad7c81 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikker tilstand"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personlig"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Arbejde"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Personlige apps"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android til arbejdsbrug"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Tjenester, der koster dig penge"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Gør ting, der kan koste dig penge."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Dine beskeder"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Tillader, at appen opfanger og omdirigerer et lydoutput."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Registrering af kommandoord"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Tillader, at appen optager lyd til registrering af kommandoord. Optagelsen kan ske i baggrunden, men forhindrer ikke andre lydoptagelser (f.eks. videokamera)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Lydhåndtering"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Tillader, at appen direkte kontrollerer lydhåndteringen og tilsidesætter beslutninger, som foretages af lydpolitikker."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"opfang et videooutput"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Tillader, at appen opfanger og omdirigerer et videooutput."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"opfang et sikkert videooutput"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Tillader, at en applikation styrer nøglebeskyttelsen."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Registrere ændringer i trust-tilstand."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Tillader, at en applikation registrerer ændringer i trust-tilstand."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Angiv en tillidsagent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Tillader, at en applikation angiver en tillidsagent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Knytte sig til en trust agent-tjeneste"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Tillader, at en applikation knytter sig til en trust agent-tjeneste."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interager med opdaterings- og gendannelsessystemet"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 70c12cd..cc1f042 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Abgesicherter Modus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-System"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Privat"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Geschäftlich"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Private Apps"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android für Geschäftliches"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Kostenpflichtige Dienste"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Kostenpflichtige Aktionen"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Ihre Nachrichten"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Ermöglicht der App die Erfassung und Weiterleitung von Audioausgaben"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Hotword-Erkennung"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"App darf Audio für die Hotword-Erkennung erfassen. Dies kann im Hintergrund durchgeführt werden und beeinflusst die Erfassung von Audio über andere Funktionen (z. B. Camcorder) nicht."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Audiorouting"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Ermöglicht der App die direkte Steuerung des Audiorouting und das Außerkraftsetzen von Entscheidungen der Audiorichtlinien"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"Videoausgabe erfassen"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Ermöglicht der App die Erfassung und Weiterleitung von Videoausgaben"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"Sichere Videoausgabe erfassen"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Apps können den Keyguard steuern."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Überwachung von Änderungen des Trust-Status"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Ermöglicht einer App die Überwachungen von Änderungen des Trust-Status"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Trust Agent bereitstellen"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ermöglich die Bereitstellung eines Trust Agents durch eine App"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"An Trust Agent-Service anbinden"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ermöglicht einer App die Anbindung an einen Trust Agent-Service"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Mit Update- und Wiederherstellungssystem interagieren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d0561b9..a04635d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Προσωπικό"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Εργασία"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Προσωπικές εφαρμογές"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android for Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Υπηρεσίες επί πληρωμή"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Πραγματοποίηση ενεργειών για τις οποίες ενδέχεται να χρεωθείτε."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Τα μηνύματά σας"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Δίνει στην εφαρμογή τη δυνατότητα εγγραφής και ανακατεύθυνσης εξόδου ήχου."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Ανίχνευση ενεργών λέξεων"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Επιτρέπει στην εφαρμογή την εγγραφή ήχου για ανίχνευση ενεργών λέξεων. Η εγγραφή μπορεί να πραγματοποιηθεί στο παρασκήνιο, αλλά δεν εμποδίζει την εγγραφή ήχου από άλλες πηγές (π.χ. βιντεοκάμερα)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Δρομολόγηση ήχου"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Επιτρέπει στην εφαρμογή να ελέγχει άμεσα τη δρομολόγηση ήχου και αντικαθιστά τις αποφάσεις πολιτικής ήχου."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"έγγραφή εξόδου βίντεο"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Δίνει στην εφαρμογή τη δυνατότητα εγγραφής και ανακατεύθυνσης εξόδου βίντεο."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"έγγραφή ασφαλούς εξόδου βίντεο"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Επιτρέπει σε μια εφαρμογή τον έλεγχο του κλειδώματος πληκτρολογίου."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Αντίληψη αλλαγών καταστάσεων εμπιστοσύνης."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Επιτρέπει σε μια εφαρμογή να αντιλαμβάνεται τις αλλαγές στην κατάσταση εμπιστοσύνης."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Παράσχετε έναν αξιόπιστο αντιπρόσωπο."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Επιτρέπει σε μια εφαρμογή να προσφέρει έναν αξιόπιστο αντιπρόσωπο."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Σύνδεση σε υπηρεσία trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Επιτρέπει σε μια εφαρμογή να συνδεθεί σε μια υπηρεσία trust agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Αλληλεπίδραση με το σύστημα ενημέρωσης και ανάκτησης"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 1237f38..24ae577 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Work"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Personal apps"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android for Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Services that cost you money"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Do things that can cost you money."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Your messages"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Allows the app to capture and redirect audio output."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Hotword detection"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Allows the app to capture audio for Hotword detection. The capture can happen in the background but does not prevent other audio capture (e.g. Camcorder)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Audio Routing"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Allows the app to directly control audio routing and override audio policy decisions."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"capture video output"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Allows the app to capture and redirect video output."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"capture secure video output"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Allows an application to control keyguard."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Listen to trust state changes."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Allows an application to listen for changes in trust state."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Provide a trust agent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Allows an application to provide a trust agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bind to a trust agent service"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Allows an application to bind to a trust agent service."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interact with update and recovery system"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 1237f38..24ae577 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Work"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Personal apps"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android for Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Services that cost you money"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Do things that can cost you money."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Your messages"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Allows the app to capture and redirect audio output."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Hotword detection"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Allows the app to capture audio for Hotword detection. The capture can happen in the background but does not prevent other audio capture (e.g. Camcorder)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Audio Routing"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Allows the app to directly control audio routing and override audio policy decisions."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"capture video output"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Allows the app to capture and redirect video output."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"capture secure video output"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Allows an application to control keyguard."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Listen to trust state changes."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Allows an application to listen for changes in trust state."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Provide a trust agent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Allows an application to provide a trust agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bind to a trust agent service"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Allows an application to bind to a trust agent service."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interact with update and recovery system"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 0f272c4..3e5538c 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabajo"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Aplicaciones personales"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android para el trabajo"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Servicios que te cuestan dinero"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Permite que las aplicaciones realicen actividades con cargo."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Tus mensajes"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Permite que la aplicación capture y redirija la salida de audio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Detectar palabras activas"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Permite que la aplicación capture audio para la detección de palabras activas. La captura puede realizarse en segundo plano, pero no impide otras capturas de audio (por ejemplo, de la videocámara)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Enrutamiento de audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Permite que la aplicación controle directamente el enrutamiento de audio y anule las decisiones de políticas de audio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"Capturar salida de video"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Permite que la aplicación capture y redirija la salida de video."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"Capturar salida de video segura"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite que una aplicación controle los bloqueos."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Detectar cambios en estado de confianza"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que una aplicación detecte cambios en el estado de confianza."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Proporcionar un agente de confianza"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que una aplicación proporcione un agente de confianza."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Vincular con un servicio de agente de confianza"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que una aplicación se vincule con un servicio de agente de confianza."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interaccionar con el sistema de recuperación y las actualizaciones"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index cfc8b61..18b321f 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"> 999"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabajo"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Aplicaciones personales"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android para el trabajo"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Servicios por los que tienes que pagar"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Hacer acciones por las que puede que tengas que pagar"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Tus mensajes"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Permite que la aplicación capture y redirija la salida de audio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Detectar palabras activas"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Permite que la aplicación grabe audio para detectar palabras activas. La grabación se puede realizar en segundo plano pero no impide que se grabe otro tipo de audio (p.ej. la videocámara)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Enrutamiento de audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Permite que la aplicación controle directamente el enrutamiento de audio y anule las decisiones de políticas de audio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"capturar salida de vídeo"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Permite que la aplicación capture y redirija la salida de vídeo."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"capturar salida de vídeo segura"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite que una aplicación controle los bloqueos."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Detectar cambios en el estado de confianza."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que una aplicación detecte cambios en el estado de confianza."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Proporcionar un agente de confianza."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que una aplicación proporcione un agente de confianza."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Enlazar con un servicio de agente de confianza"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite a una aplicación enlazar con un servicio de agente de confianza."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interactuar con el sistema de recuperación y las actualizaciones"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 8c2112a..922a9af1 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Turvarežiim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-süsteem"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Isiklik"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Töö"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Isiklikud rakendused"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android töö jaoks"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Tasulised teenused"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Tasuliste toimingute tegemine."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Teie sõnumid"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Lubab rakendusel jäädvustada ja ümber suunata heliväljundit."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Otsetee sõna tuvastamine"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Lubab rakendusel jäädvustada heli otsetee sõna tuvastamiseks. Jäädvustamine võib toimuda taustal, kuid see ei takista muud heli jäädvustamist (nt videokaameraga)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Heli marsruutimine"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Võimaldab rakendusel otse juhtida heli marsruutimist ja alistada helieeskirjade otsused."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"videoväljundi jäädvustamine"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Lubab rakendusel jäädvustada ja ümber suunata videoväljundit."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"kaitstud videoväljundi jäädvustamine"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Lubab rakendusel võtmekaitset juhtida."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Usaldusväärse oleku muudatuste tuvastamine."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Lubab rakendusel tuvastada muudatusi usaldusväärses olekus."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Usaldusväärse agendi esitamine."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Võimaldab rakendusel esitada usaldusväärset agenti."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Usaldusväärse agendi teenusega sidumine"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Lubab rakendusel ennast siduda usaldusväärse agendi teenusega."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Suhtlemine värskenduse ja taastesüsteemiga"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 2a03ca8..0d3289e 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"شخصی"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"محل کار"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"برنامههای شخصی"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android برای کار"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"سرویسهای غیر رایگان"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"انجام کارهایی که برای شما هزینه دارد."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"پیامهای شما"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"به برنامه امکان میدهد خروجی صدا را ضبط و هدایت کند."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"تشخیص کلیدگفته"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"به برنامه اجازه میدهد تا صدا را برای تشخیص کلیدگفته ضبط کند. ضبط صدا میتواند در پسزمینه رخ دهد اما از ضبط صداهای دیگر (مثلاً دوربین فیلمبرداری) جلوگیری نمیکند."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"مسیریابی صوتی"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"به برنامه امکان میدهد تا به طور مستقیم مسیریابی صوتی را کنترل و تصمیمات مربوط به خطمشی صوتی را بازنویسی کند."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"ضبط خروجی ویدیو"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"به برنامه امکان میدهد خروجی ویدیو را ضبط و هدایت کند."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"ضبط خروجی ویدیوی ایمن"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"اجازه میدهد برنامهای محافظ کلید را کنترل کند."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"گوش دادن به تغییرات وضعیت trust."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"به یک برنامه کاربردی برای گوش دادن به تغییرات در trust اجازه میدهد."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"یک عامل مورد اعتماد فراهم میآورد."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"به برنامه امکان میدهد یک عامل مورد اعتماد فراهم آورد."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"اتصال به یک سرویس trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"به یک برنامه کاربردی برای اتصال به یک سرویس trust agent اجازه میدهد."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"تعامل با سیستم بهروزرسانی و بازیابی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 9398cfa..1f4418c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Henkilökohtainen"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Työ"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Omat sovellukset"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android työkäyttöön"</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>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Antaa sovellukselle luvan äänentoiston kaappaamiseen ja uudelleenohjaamiseen."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Toimintosanan tunnistus"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Antaa sovelluksen siepata ääntä toimintosanojen tunnistusta varten. Sieppaus voi tapahtua taustalla, mutta se ei estä muita laitteita, kuten videokameraa, käyttämästä ääntä."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Äänen reititys"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Antaa sovelluksen hallita äänen reititystä suoraan ja ohittaa ääniä koskevia sääntöjä."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"videokuvan kaappaus"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Antaa sovellukselle luvan videokuvan kaappaamiseen ja uudelleenohjaamiseen"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"suojatun videokuvan kaappaus"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Antaa sovelluksen hallita näppäinvahtia."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Seuraa luottamuksen tilamuutoksia."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Antaa sovelluksen seurata luottamuksen tilamuutoksia."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Luotettavan tahon tarjoaminen"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Antaa sovelluksen tarjota luotettavan tahon."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Luotettavaan tahoon sitoutuminen"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Antaa sovelluksen sitoutua luotettavaan tahoon."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Vuorovaikutus päivitys- ja palautusjärjestelmän kanssa"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 9aa2cfe..3b30f65 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personnel"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Travail"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Applications personnelles"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android pour les activités professionnelles"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Services payants"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Effectuer des opérations payantes"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vos messages"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Autoriser l\'application à capturer et à rediriger la sortie audio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Détection de mots clés"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Permet à l\'application de capturer de l\'audio pour la détection de mots clés. La capture peut s\'effectuer en arrière-plan, et n\'empêche pas les autres opérations de capture audio (par exemple, avec un caméscope)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Acheminement de l\'audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Permet à l\'application de contrôler directement l\'acheminement audio et de remplacer les décisions concernant les politiques audio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"capturer la sortie vidéo"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Autoriser l\'application à capturer et à rediriger la sortie vidéo."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"capturer la sortie vidéo sécurisée"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permet à une application de contrôler la protection des touches."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Détecter les modifications de l\'état de confiance"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permet à une application de détecter les modifications de l\'état de confiance."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fournir un agent de confiance."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permet à une application de fournir un agent de confiance."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Lier à un service d\'agent de confiance"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permet à une application de se lier à un service d\'agent de confiance."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir avec le système de récupération et de mise à jour"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3d043c4..abc8f9a 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode sécurisé"</string>
<string name="android_system_label" msgid="6577375335728551336">"Système Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personnel"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Professionnel"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Applications personnelles"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android pour les activités professionnelles"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Services payants"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Effectuer des opérations payantes"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vos messages"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Autoriser l\'application à enregistrer et à rediriger les sorties audio"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Détection de mot clé"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Permet à l\'application de capturer du contenu audio pour détecter des mots clés. L\'enregistrement peut se produire en arrière-plan, sans désactiver les autres services de capture audio (tels que ceux d\'un caméscope)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Routage de l\'audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Permettre à l\'application de contrôler directement le routage de l\'audio et de remplacer les décisions relatives aux règles concernant l\'audio"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"enregistrer les sorties vidéo"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Autoriser l\'application à enregistrer et à rediriger les sorties vidéo"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"enregistrer les sorties vidéo sécurisées"</string>
@@ -1171,7 +1173,7 @@
<string name="chooseUsbActivity" msgid="6894748416073583509">"Sélectionnez une application pour le périphérique de stockage USB"</string>
<string name="noApplications" msgid="2991814273936504689">"Aucune application ne peut effectuer cette action."</string>
<string name="aerr_title" msgid="1905800560317137752"></string>
- <string name="aerr_application" msgid="932628488013092776">"L\'application \"<xliff:g id="APPLICATION">%1$s</xliff:g>\" s\'est arrêtée."</string>
+ <string name="aerr_application" msgid="932628488013092776">"\"<xliff:g id="APPLICATION">%1$s</xliff:g>\" s\'est arrêté."</string>
<string name="aerr_process" msgid="4507058997035697579">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> s\'est interrompu."</string>
<string name="anr_title" msgid="4351948481459135709"></string>
<string name="anr_activity_application" msgid="1904477189057199066">"L\'application <xliff:g id="APPLICATION">%2$s</xliff:g> ne répond pas.\n\nVoulez-vous quitter ?"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permet à une application de contrôler la protection des touches."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Détecter les modifications de l\'état de confiance"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permettre à une application de détecter les modifications de l\'état de confiance."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fournir un agent de confiance"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permettre à une application de fournir un agent de confiance"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"S\'associer à un service d\'agent de confiance"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permettre à une application de s\'associer à un service d\'agent de confiance."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir avec le système de récupération et de mise à jour"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index f9bd61a..ef2205b 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"व्यक्तिगत"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"कार्यालय"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"व्यक्तिगत ऐप्स"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"कार्यालय के लिए Android"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"वे सेवाएं जिन पर आप खर्चा करते हैं"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"ऐसे कार्य करें जिससे आपका धन खर्च हो सकता है."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"आपके संदेश"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"ऐप्स को ऑडियो आउटपुट को कैप्चर और रीडायरेक्ट करने देता है."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"हॉटवर्ड पहचान"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"एप्लिकेशन को हॉटवर्ड पहचान के लिए ऑडियो कैप्चर करने देती है. कैप्चर पृष्ठभूमि में हो सकता है लेकिन वह अन्य ऑडियो कैप्चर (उदा. कैमकॉर्डर) को नहीं रोकता."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"ऑडियो रूटिंग"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"ऐप्स को ऑडियो रूटिंग पर प्रत्यक्ष नियंत्रण करने देती है और ऑडियो नीति निर्णयों को ओवरराइड करने देती है."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"वीडियो आउटपुट को कैप्चर करें"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"ऐप्स को वीडियो आउटपुट को कैप्चर और रीडायरेक्ट करने देता है."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"सुरक्षित वीडियो आउटपुट को कैप्चर करें"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"ऐप्स को कीगार्ड नियंत्रित करने देती है."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"ट्रस्ट स्थिति बदलावों को सुनें."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"किसी एप्लिकेशन को ट्रस्ट स्थिति के बदलावों को सुनने की अनुमति देती है."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"विश्वसनीय एजेंट प्रदान करें."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"एप्लिकेशन को विश्वसनीय एजेंट प्रदान करने देती है."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"ट्रस्ट एजेंट सेवा से आबद्ध करना"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"किसी एप्लिकेशन को ट्रस्ट एजेंट सेवा से आबद्ध करने की अनुमति देती है."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"अपडेट और पुनर्प्राप्ति सिस्टम के साथ सहभागिता करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 2adaaf1..6badaac 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Siguran način rada"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sustav Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osobno"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Posao"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Osobne aplikacije"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android za rad"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Usluge koje se plaćaju"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Radite stvari koje će uzrokovati novčane troškove."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vaše poruke"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Omogućuje aplikaciji primanje i preusmjeravanje audioizlaza."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Otkrivanje pokretača značajke"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Aplikaciji omogućuje snimanje zvuka radi otkrivanja pokretača značajke. Snimanje se može odvijati u pozadini, ali ne sprječava drugo snimanje zvuka (npr. kameru)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Preusmjeravanje audija"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Omogućuje aplikaciji izravno upravljanje preusmjeravanjem audija i premošćivanje odluka pravila o audiju."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"primanje videoizlaza"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Omogućuje aplikaciji primanje i preusmjeravanje videoizlaza."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"primanje sigurnog videoizlaza"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Omogućuje aplikaciji upravljanje zaključavanjem tipkovnice."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Prati promjene pouzdanog stanja."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Omogućuje aplikaciji praćenje promjena pouzdanog stanja."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Pružanje pouzdanog predstavnika."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Omogućuje aplikaciji pružanje pouzdanog predstavnika."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Povezivanje s uslugom pouzdanog predstavnika"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Omogućuje aplikaciji povezivanje s uslugom pouzdanog predstavnika."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakcija s ažuriranjem i sustavom za oporavak"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 45573e6..6d0ff1b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Biztonsági üzemmód"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android rendszer"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Személyes"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Munkahelyi"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Személyes alkalmazások"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android munkához"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Fizetős szolgáltatások"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Olyan dolgok végrehajtása, amelyek pénzbe kerülnek."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Saját üzenetek"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Engedélyezi az alkalmazásnak a hangkimenet rögzítését és átirányítását."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Hotwordérzékelés"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Lehetővé teszi, hogy alkalmazás rögzítse a befelé jövő hangokat hotwordérzékelés céljából. A rögzítés végbemehet a háttérben, és nem zavarja a más jellegű hangrögzítést, például a kamera esetében."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Hangirányítás"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Lehetővé teszi az alkalmazás számára, hogy kezelje a hangirányítást, illetve felülírja a hangra vonatkozó irányelvi döntéseket."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"videokimenet rögzítése"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Engedélyezi az alkalmazásnak a videokimenet rögzítését és átirányítását."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"biztonságos videokimenet rögzítése"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Lehetővé teszi egy alkalmazás számára a billentyűzár vezérlését."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Trust-állapot változásának figyelése"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Lehetővé teszi, hogy az alkalmazás figyelje a trust-állapot változásait."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Trust agent szoftver megadása"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Lehetővé teszi, hogy az alkalmazás megadjon egy trust agent szoftvert."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Csatlakozás egy trust agent szolgáltatáshoz"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Lehetővé teszi, hogy az alkalmazás egy trust agent szolgáltatáshoz csatlakozzon."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Kapcsolatfelvétel a frissítési és helyreállítási rendszerrel"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 2d72f06..5591bfb 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Անձնական"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Աշխատանքային"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Անձնական ծրագրեր"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android for Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Ծառայություններ, որոնց համար կգանձվեք"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Կատարել գործողություններ, որի դիմաց ձեր հաշվից գումար կծախսվի:"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Ձեր հաղորդագրությունները"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Թույլ է տալիս ծրագրին պահել և վերահղել աուդիո արտածումը:"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Թեժ բառի հայտնաբերում"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Հավելվածին թույլ է տալիս որսալ ձայնանյութը՝ թեժ բառի հայտնաբերման համար: Դա կարող է տեղի ունենալ հետնաշերտում, սակայն չի խանգարի այլ աուդիո ձայնագրություններին (օր.՝ Տեսախցիկից):"</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Աուդիո երթուղայնացում"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Ծրագրին թույլ է տալիս անմիջականորեն վերահսկել աուդիո երթուղայնացումը և վերասահմանել աուդիո քաղաքականության որոշումները:"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"պահել վիդեո արտածումը"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Թույլ է տալիս ծրագրին պահել և վերահղել վիդեո արտածումը:"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"պահել անվտանգ վիդեո արտածումը"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Թույլ է տալիս հավելվածին կառավարել ստեղնաշարի պաշտպանիչը:"</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Լսել վստահության կարգավիճակի փոփոխությունները:"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Ծրագրին թույլ է տալիս լսել վստահության կարգավիճակի փոփոխությունները:"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Տրամադրել վստահելի գործակալ:"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ծրագրին թույլ է տալիս տրամադրել վստահելի գործակալ:"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Կապվել վստահելի գործակալի ծառայությանը"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ծրագրին թույլ է տալիս կապվել վստահելի գործակալի ծառայությանը:"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Փոխազդել թարմացման և վերականգնման համակարգի հետ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 3d619b9..52da0cb 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Mode aman"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Pribadi"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Kantor"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Aplikasi pribadi"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android untuk Bekerja"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Layanan berbayar"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Lakukan hal yang dapat dikenai biaya."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Pesan Anda"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Memungkinkan aplikasi menangkap dan mengalihkan keluaran audio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Deteksi kata cepat"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Memungkinkan aplikasi menangkap audio untuk deteksi Kata Cepat. Penangkapan dapat berlangsung di latar belakang namun tidak akan mencegah penangkapan audio yang lain (misalnya Perekam video)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Perutean Audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Memungkinkan aplikasi mengontrol perutean audio dan mengganti keputusan kebijakan audio secara langsung."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"tangkap keluaran video"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Memungkinkan aplikasi menangkap dan mengalihkan keluaran video."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"tangkap keluaran video aman"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Izinkan aplikasi untuk mengontrol pengaman."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Dengarkan perubahan status kepercayaan."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Mengizinkan aplikasi mendengarkan perubahan dalam status kepercayaan."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Berikan agen tepercaya."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Memungkinkan aplikasi memberikan agen tepercaya."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Ikat ke layanan agen kepercayaan"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Mengizinkan aplikasi mengikat ke layanan agen kepercayaan."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Berinteraksi dengan sistem pemulihan dan pembaruan"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 9a26b62..9ef396e 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Modalità provvisoria"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personale"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Lavoro"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"App personali"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android for Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Servizi che prevedono un costo"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Svolgono operazioni che possono comportare un costo."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"I tuoi messaggi"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Consente all\'app di acquisire e reindirizzare l\'uscita audio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Rilevamento hotword"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Consente all\'app di acquisire l\'audio per il rilevamento Hotword. L\'acquisizione può avvenire in background ma non impedisce l\'acquisizione di altro audio (ad esempio con la videocamera)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Routing dell\'audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Consente all\'app di controllare direttamente il routing audio e di sostituire le impostazioni delle norme per l\'audio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"acquisizione dell\'uscita video"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Consente all\'app di acquisire e reindirizzare l\'uscita video."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"acquisizione dell\'uscita video sicura"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Consente a un\'applicazione di controllare keguard."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Rilevamento modifiche dello stato trust."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Consente a un\'applicazione di rilevare le modifiche nello stato trust."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Indica un trust agent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Consente a un\'applicazione di indicare un trust agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Associazione a un servizio trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Consente a un\'applicazione di associarsi a un servizio trust agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interazione con il sistema di ripristino e aggiornamento"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 5c47131..c7cbe56 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"אישי"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"עבודה"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"אפליקציות אישיות"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android לעבודה"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"שירותים שעולים כסף"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"ביצוע פעולות שעשויות לעלות לך כסף."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"ההודעות שלך"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"מאפשרת לאפליקציה לקלוט ולהפנות מחדש פלט אודיו."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"זיהוי של מילת הפעלה"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"מאפשרת לאפליקציה לקלוט אודיו עבור זיהוי של מילת הפעלה. פעולת הקליטה יכולה להתבצע ברקע, אבל לא מונעת קליטת אודיו אחרת (למשל, במצלמת הווידאו)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"ניתוב אודיו"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"מאפשר לאפליקציה לשלוט באופן ישיר בניתוב אודיו ולעקוף החלטות מדיניות עבור אודיו."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"קליטת פלט וידאו"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"מאפשרת לאפליקציה לקלוט ולהפנות מחדש פלט וידאו."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"קליטת פלט וידאו מאובטח"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"מאפשר לאפליקציה לשלוט במגן המקלדת."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"חיפוש שינויים במצב אמון."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"מאפשר לאפליקציה לחפש שינויים במצב אמון."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"ציון סוכן אמון."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"מאפשר לאפליקציה לספק סוכן אמון."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"איגוד אל שירות סוכן אמון"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"מאפשר לאפליקציה לאגוד אל שירות סוכן אמון."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"אינטראקציה עם מערכת שחזור ועדכונים"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 6d35633..281aaa9 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"プライベート"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"職場"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"プライベートアプリ"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"ビジネス向けAndroid"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"料金の発生するサービス"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"料金発生の可能性がある操作を実行します。"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"送受信したメッセージ"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"音声出力のキャプチャとリダイレクトをアプリに許可します。"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"注目ワード検出"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"注目ワード検出での音声キャプチャをアプリに許可します。キャプチャはバックグラウンドで発生しますが、その他の音声キャプチャ(例: ビデオ録画)を妨げることはありません。"</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"オーディオルーティング"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"オーディオルーティングの直接制御とオーディオポリシー決定の上書きをアプリに許可します。"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"動画出力のキャプチャ"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"動画出力のキャプチャとリダイレクトをアプリに許可します。"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"セキュリティ保護された動画出力のキャプチャ"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"キーガードの制御をアプリに許可します。"</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"信頼状態の変更をリッスン"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"信頼状態の変更をリッスンすることをアプリに許可します。"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"信頼できるエージェントの提供"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"信頼できるエージェントの提供をアプリに許可します。"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"信頼できるエージェントサービスへのバインド"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"信頼できるエージェントサービスにバインドすることをアプリに許可します。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"アップデートと回復システムへのアクセス"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 770c14d..01e68a2 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"პირადი"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"სამსახური"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"პერსონალური აპები"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android სამსახურისთვის"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"სერვისები, რომელშიც ფულის გადახდა გიწევთ"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"ისეთი აქტივობების განხორციელება, რომლებშიც ფულის გადახდა მოგიწევთ."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"თქვენი შეტყობინებები"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"საშუალებას აძლევს აპს დაიჭიროს და გადაამისამართოს გამომავალი აუდიო."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"ჯადოსნური სიტყვის პოვნა"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"საშუალებას აძლევს აპს ჩაიწეროს აუდიო ჯადოსნური სიტყვების ამოცნობისათვის. ჩაწერა შესაძლოა განხორციელდეს ფონურად, თუმცა ხელს არ უშლის სხვა სახის აუდიოს ჩაწერას (მაგ. ვიდეოჩამწერიდან)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"აუდიო მარშრუტიზაცია"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"საშუალებას აძლევს აპს პირდაპირ აკონტროლოს აუდიო მარშრუტიზაცია უკუაგდოს აუდიო პოლისის გადაწყვეტილებები."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"გამომავალი ვიდეოს დაჭერა"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"საშუალებას აძლევს აპს დაიჭიროს და გადაამისამართოს გამომავალი ვიდეო."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"გამომავალი დაცული ვიდეოს დაჭერა"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"აპლიკაციას შეეძლება ღილაკების დამცავის კონტროლი."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"ნდობის მდგომარეობის ცვლილებების მოსმენა."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"საშუალებას აძლევს აპლიკაციას მოუსმინოს ცვლილებებს სანდო მდგომარეობაში."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"სანდო აგენტის წარმოდგენა."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"საშუალებას აძლევს აპლიკაციას წარმოადგინოს სანდო აგენტი."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"სანდო აგენტის სერვისზე მიმაგრება."</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"საშუალებას აძლევს აპლიკაციას მიემაგროს სანდო აგენტის სერვისს."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"განახლებასთან და აღდგენის სისტემასთან ინტერაქცია"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index f3b8495..ec389b1 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -68,7 +68,7 @@
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
- <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string>
+ <string name="ClipMmi" msgid="6952821216480289285">"លេខសម្គាល់អ្នកហៅចូល"</string>
<string name="ClirMmi" msgid="7784673673446833091">"លេខសម្គាល់អ្នកហៅចេញ"</string>
<string name="CfMmi" msgid="5123218989141573515">"បញ្ជូនការហៅបន្ត"</string>
<string name="CwMmi" msgid="9129678056795016867">"រង់ចាំការហៅ"</string>
@@ -125,7 +125,7 @@
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិនបានបញ្ជូនបន្ត"</string>
<string name="fcComplete" msgid="3118848230966886575">"កូដលក្ខណៈពេញលេញ។"</string>
<string name="fcError" msgid="3327560126588500777">"បញ្ហាការតភ្ជាប់ ឬកូដលក្ខណៈមិនត្រឹមត្រូវ។"</string>
- <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string>
+ <string name="httpErrorOk" msgid="1191919378083472204">"យល់ព្រម"</string>
<string name="httpError" msgid="7956392511146698522">"មានកំហុសបណ្ដាញ។"</string>
<string name="httpErrorLookup" msgid="4711687456111963163">"រកមិនឃើញ URL ។"</string>
<string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍ផ្ទៀងផ្ទាត់តំបន់បណ្ដាញមិនត្រូវបានគាំទ្រ។"</string>
@@ -183,19 +183,19 @@
<string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើកសំឡេង"</string>
<string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេលជិះយន្តហោះ"</string>
<string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បានបើករបៀបពេលជិះយន្តហោះ"</string>
- <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string>
+ <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បានបិទរបៀបពេលជិះយន្តហោះ"</string>
<string name="global_action_settings" msgid="1756531602592545966">"ការកំណត់"</string>
<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>
- <string name="user_owner_label" msgid="2804351898001038951">"ផ្ទាល់ខ្លួន"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"កន្លែងធ្វើការ"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"កម្មវិធីផ្ទាល់ខ្លួន"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android សម្រាប់ការងារ"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"សេវាកម្មដែលកាត់លុយរបស់អ្នក"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"ធ្វើអ្វីដែលអាចកាត់លុយរបស់អ្នក។"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"សាររបស់អ្នក"</string>
<string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និងសរសេរសារ SMS, អ៊ីមែល និងសារផ្សេងៗទៀតរបស់អ្នក។"</string>
<string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មានផ្ទាល់ខ្លួនរបស់អ្នក"</string>
- <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string>
+ <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីអ្នក ដែលបានរក្សាទុកក្នុងកាតទំនាក់ទំនងរបស់អ្នក។"</string>
<string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មានសង្គមរបស់អ្នក"</string>
<string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូលដំណើរការព័ត៌មានដោយផ្ទាល់អំពីទំនាក់ទំនង និងការភ្ជាប់សង្គមរបស់អ្នក។"</string>
<string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំងរបស់អ្នក"</string>
@@ -384,7 +384,7 @@
<string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យកម្មវិធីមើលគ្រាប់ចុចដែលអ្នកចុចពេលមានអន្តរកម្មជាមួយកម្មវិធីផ្សេង (ដូចជា បញ្ចូលពាក្យសម្ងាត់)។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចងទៅវិធីសាស្ត្របញ្ចូល"</string>
<string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃវិធីសាស្ត្របញ្ចូល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string>
+ <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចងសេវាកម្មភាពមធ្យោបាយងាយស្រួល"</string>
<string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មភាពងាយស្រួល។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindPrintService" msgid="8462815179572748761">"ចងសេវាកម្មបោះពុម្ព"</string>
<string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
@@ -402,7 +402,7 @@
<string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលរបស់សេវាកម្មអន្តរកម្មសំឡេង។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់ទៅការបង្ហាញពីចម្ងាយ"</string>
<string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតកំពូលនៃការបង្ហាញពីចម្ងាយ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string>
+ <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចងសេវាកម្មធាតុក្រាហ្វិក"</string>
<string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យម្ចាស់ចងចំណុចប្រទាក់កម្រិតកំពូលនៃសេវាកម្មធាតុក្រាហ្វិក។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់ទៅសេវាកម្មក្រុមហ៊ុនផ្ដល់ច្រក"</string>
<string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅក្រុមហ៊ុនផ្ដល់ច្រកដែលបានចុះឈ្មោះ។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
@@ -410,7 +410,7 @@
<string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យម្ចាស់ផ្ញើគោលបំណងទៅអ្នកគ្រប់គ្រងឧបករណ៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់ទៅការបញ្ចូលទូរទស្សន៍"</string>
<string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាតឲ្យម្ចាស់ភ្ជាប់ទៅចំណុចប្រទាក់កម្រិតខ្ពស់នៃការបញ្ចូលទូរទស្សន៍។ មិនគួរចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
- <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string>
+ <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍"</string>
<string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាតឲ្យម្ចាស់បន្ថែម ឬលុបកម្មវិធីគ្រប់គ្រងឧបករណ៍សកម្មចេញ។ មិនគួរប្រើសម្រាប់កម្មវិធីធម្មតាទេ។"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរទិសអេក្រង់"</string>
<string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យកម្មវិធីប្ដូរការបង្វិលអេក្រង់នៅពេលណាមួយ។ មិនចាំបាច់សម្រាប់កម្មវិធីធម្មតាទេ។"</string>
@@ -422,9 +422,9 @@
<string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យកម្មវិធីស្នើសញ្ញាដែលបានផ្ដល់ត្រូវផ្ញើទៅដំណើរការស្ថិតស្ថេរទាំងអស់។"</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើឲ្យកម្មវិធីដំណើរការជានិច្ច"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យកម្មវិធីធ្វើជាផ្នែកស្ថិតស្ថេរដោយខ្លួនឯងក្នុងអង្គចងចាំ។ វាអាចកំណត់អង្គចងចាំដែលអាចប្រើបានចំពោះកម្មវិធីផ្សេងៗ ដោយធ្វើឲ្យកុំព្យូទ័របន្ទះយឺត។"</string>
- <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string>
+ <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យកម្មវិធី ធ្វើជាផ្នែកអចិន្ត្រៃយ៍នៃខ្លួនក្នុងអង្គចងចាំ។ វាអាចកម្រិតអង្គចងចាំអាចប្រើបាន ដើម្បីធ្វើឲ្យកម្មវិធីផ្សេងធ្វើឲ្យទូរស័ព្ទរបស់អ្នកយឺត។"</string>
<string name="permlab_deletePackages" msgid="184385129537705938">"លុបកម្មវិធី"</string>
- <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។"</string>
+ <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យកម្មវិធីលុបកញ្ចប់ Android ។ កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុបកម្មវិធីសំខាន់ៗ។ "</string>
<string name="permlab_clearAppUserData" msgid="274109191845842756">"លុបទិន្នន័យរបស់កម្មវិធីផ្សេង"</string>
<string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យកម្មវិធីសម្អាតទិន្នន័យអ្នកប្រើ។"</string>
<string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុបឃ្លាំងសម្ងាត់កម្មវិធីផ្សេងៗ"</string>
@@ -475,7 +475,7 @@
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងកុំព្យូទ័របន្ទះ រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនងជាក់លាក់។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនងរបស់អ្នក។"</string>
<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យកម្មវិធីកែទិន្នន័យអំពីទំនាក់ទំនងរបស់អ្នកដែលបានរក្សាទុកក្នុងទូរស័ព្ទរបស់អ្នក រួមមានប្រេកង់ដែលអ្នកបានហៅ អ៊ីមែល ឬបានទាក់ទងតាមវិធីផ្សេងៗជាមួយទំនាក់ទំនាក់ជាក់លាក់។ សិទ្ធិនេះឲ្យកម្មវិធីលុបទិន្នន័យទំនាក់ទំនង។"</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"អានកំណត់ហេតុហៅ"</string>
- <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
+ <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យកម្មវិធីអានបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
<string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យកម្មវិធីអានបញ្ជីហៅទូរស័ព្ទរបស់អ្នក រួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។ សិទ្ធិនេះអនុញ្ញាតឲ្យកម្មវិធីរក្សាទុកទិន្នន័យបញ្ជីហៅរបស់អ្នក ហើយកម្មវិធីព្យាបាទអាចចែករំលែកទិន្នន័យបញ្ជីហៅដោយមិនឲ្យអ្នកដឹង។"</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរបញ្ជីហៅ"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យកម្មវិធីកែបញ្ជីហៅកុំព្យូទ័របន្ទះរបស់អ្នករួមមានទិន្នន័យអំពីការហៅចូល និងចេញ។កម្មវិធីព្យាបាទអាចប្រើវា ដើម្បីលុប ឬកែបញ្ជីហៅរបស់អ្នក។"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"ឱ្យកម្មវិធីដើម្បីចាប់យក និងប្ដូរទិសលទ្ធផលអូឌីយ៉ូ។"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"ការរកឃើញពាក្យ"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"ឲ្យកម្មវិធីថតអូឌីយ៉ូសម្រាប់ការរកឃើញពាក្យ។ ការថតអាចកើតឡើងក្នុងផ្ទៃខាងក្រោយ ប៉ុន្តែមិនរារាំងការថតអូឌីយ៉ូផ្សេងទេ (ឧ. ម៉ាស៊ីនថតវីដេអូ)។"</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"ការនាំផ្លូវអូឌីយ៉ូ"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"ឲ្យកម្មវិធីពិនិត្យផ្លូវអូឌីយ៉ូដោយផ្ទាល់ ហើយបដិសេធការសម្រេចចិត្តគោលនយោបាយអូឌីយ៉ូ។"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"ចាប់យកលទ្ធផលវីដេអូ"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"ឲ្យកម្មវិធីចាប់យក និងប្ដូរទិសលទ្ធផលវីដេអូ។"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"ចាប់យកលទ្ធផលវីដេអូសុវត្ថិភាព"</string>
@@ -609,7 +611,7 @@
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យកម្មវិធីកំណត់ជំនួយទំហំផ្ទាំងរូបភាពប្រព័ន្ធ។"</string>
<string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់ប្រព័ន្ធទៅលំនាំដើមរោងចក្រឡើងវិញ"</string>
<string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យកម្មវិធីកំណត់ប្រព័ន្ធដូចការកំណត់ចេញពីរោងចក្រឡើងវិញពេញលេញ ដោយលុបទិន្នន័យ ការកំណត់រចនាសម្ព័ន្ធ និងកម្មវិធីបានដំឡើង។"</string>
- <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</string>
+ <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់ម៉ោង"</string>
<string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យកម្មវិធីប្ដូរម៉ោងកុំព្យូទ័របន្ទះ។"</string>
<string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យកម្មវិធីប្ដូរម៉ោងទូរស័ព្ទ។"</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់តំបន់ពេលវេលា"</string>
@@ -775,7 +777,7 @@
<string-array name="organizationTypes">
<item msgid="7546335612189115615">"កន្លែងធ្វើការ"</item>
<item msgid="4378074129049520373">"ផ្សេងៗ"</item>
- <item msgid="3455047468583965104">"តាមតម្រូវការ"</item>
+ <item msgid="3455047468583965104">"តាមតម្រូវការ"</item>
</string-array>
<string-array name="imProtocols">
<item msgid="8595261363518459565">"AIM"</item>
@@ -791,7 +793,7 @@
<string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string>
<string name="phoneTypeMobile" msgid="6501463557754751037">"ចល័ត"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែងធ្វើការ"</string>
- <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string>
+ <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារកន្លែងធ្វើការ"</string>
<string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារផ្ទះ"</string>
<string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string>
<string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string>
@@ -916,7 +918,7 @@
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បីដោះសោ ចូលគណនី Google របស់អ្នក។"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះអ្នកប្រើ (អ៊ីមែល)"</string>
- <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
+ <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -961,7 +963,7 @@
<string name="factorytest_failed" msgid="5410270329114212041">"បានបរាជ័យក្នុងការសាកល្បងរោងចក្រ"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវបានគាំទ្រសម្រាប់តែកញ្ចប់បានដំឡើងក្នុង /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"រកមិនឃើញកញ្ចប់ដែលផ្ដល់សកម្មភាព FACTORY_TEST ។"</string>
- <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string>
+ <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់ផ្ដើមឡើងវិញ"</string>
<string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រមានចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string>
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់ការរុករក"</string>
@@ -1019,7 +1021,7 @@
<string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string>
<string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string>
<string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
- <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
+ <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
<string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string>
<string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string>
<string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរកសំណួរ"</string>
@@ -1103,18 +1105,18 @@
<string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string>
<string name="preposition_for_time" msgid="5506831244263083793">"នៅម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="preposition_for_year" msgid="5040395640711867177">"ក្នុងឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string>
- <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
+ <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
<string name="days" msgid="4774547661021344602">"ថ្ងៃ"</string>
<string name="hour" msgid="2126771916426189481">"ម៉ោង"</string>
<string name="hours" msgid="894424005266852993">"ម៉ោង"</string>
- <string name="minute" msgid="9148878657703769868">"នាទី"</string>
+ <string name="minute" msgid="9148878657703769868">"នាទី"</string>
<string name="minutes" msgid="5646001005827034509">"នាទី"</string>
- <string name="second" msgid="3184235808021478">"វិនាទី"</string>
+ <string name="second" msgid="3184235808021478">"វិនាទី"</string>
<string name="seconds" msgid="3161515347216589235">"វិនាទី"</string>
- <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
- <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
- <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
- <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
+ <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
+ <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
+ <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
+ <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
<plurals name="duration_seconds">
<item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item>
<item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item>
@@ -1130,12 +1132,12 @@
<string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហាវីដេអូ"</string>
<string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូនេះមិនត្រឹមត្រូវសម្រាប់ចរន្តចូលឧបករណ៍នេះ។"</string>
<string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិនអាចចាក់វីដេអូនេះ។"</string>
- <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string>
+ <string name="VideoView_error_button" msgid="2822238215100679592">"យល់ព្រម"</string>
<string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="noon" msgid="7245353528818587908">"រសៀល"</string>
<string name="Noon" msgid="3342127745230013127">"រសៀល"</string>
<string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string>
- <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string>
+ <string name="Midnight" msgid="5630806906897892201">"កណ្ដាលអធ្រាត្រ"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="6876518925844129331">"ជ្រើសទាំងអស់"</string>
@@ -1152,13 +1154,13 @@
<string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្របញ្ចូល"</string>
<string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាពអត្ថបទ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់ទំហំផ្ទុក"</string>
- <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
+ <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
<string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងដំណើរការ"</string>
<string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ ដើម្បីមើលព័ត៌មានបន្ថែម ឬបញ្ឈប់កម្មវិធី។"</string>
- <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string>
- <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
- <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string>
- <string name="no" msgid="5141531044935541497">"បោះបង់"</string>
+ <string name="ok" msgid="5970060430562524910">"យល់ព្រម"</string>
+ <string name="cancel" msgid="6442560571259935130">"បោះបង់"</string>
+ <string name="yes" msgid="5362982303337969312">"យល់ព្រម"</string>
+ <string name="no" msgid="5141531044935541497">"បោះបង់"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string>
<string name="loading" msgid="7933681260296021180">"កំពុងផ្ទុក..."</string>
<string name="capital_on" msgid="1544682755514494298">"បើក"</string>
@@ -1167,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">"ជ្រើសកម្មវិធីសម្រាប់ឧបករណ៍យូអេសប៊ី"</string>
<string name="noApplications" msgid="2991814273936504689">"គ្មានកម្មវិធីអាចអនុវត្តសកម្មភាពនេះ។"</string>
<string name="aerr_title" msgid="1905800560317137752"></string>
@@ -1178,7 +1180,7 @@
<string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string>
<string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិនឆ្លើយតប។ តើអ្នកចង់បិទវា?"</string>
<string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិនឆ្លើយតប។ \n\nតើអ្នកចង់បិទវាឬ?"</string>
- <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string>
+ <string name="force_close" msgid="8346072094521265605">"យល់ព្រម"</string>
<string name="report" msgid="4060218260984795706">"រាយការណ៍"</string>
<string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string>
<string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រក្លាយជាមិនឆ្លើយតប។\n\nតើអ្នកចង់បិទវា?"</string>
@@ -1260,19 +1262,19 @@
<string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះអាចកាត់លុយ"</font>" លើគណនីចល័តរបស់អ្នក។"</string>
<string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វានឹងគិតថ្លៃសេវាកម្មលើគណនីចល័តរបស់អ្នក។"</font></string>
<string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string>
- <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string>
+ <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះបង់"</string>
<string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំជម្រើសរបស់ខ្ញុំ"</string>
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នកអាចប្ដូរវាពេលក្រោយក្នុងការកំណត់ > កម្មវិធី"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាតជានិច្ច"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំអនុញ្ញាត"</string>
<string name="sim_removed_title" msgid="6227712319223226185">"បានដកស៊ីមកាតចេញ"</string>
- <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string>
+ <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញចល័តនឹងប្រើលែងបានរហូតដល់អ្នកចាប់ផ្ដើមជាមួយស៊ីមកាតដែលបាបញ្ចូលត្រឹមត្រូវ។"</string>
<string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string>
<string name="sim_added_title" msgid="3719670512889674693">"បានបន្ថែមស៊ីមកាត"</string>
<string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើមឧបករណ៍របស់អ្នកឡើងវិញ ដើម្បីចូលដំណើរការបណ្ដាញចល័ត។"</string>
<string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើមឡើងវិញ"</string>
- <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string>
- <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string>
+ <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់ម៉ោង"</string>
+ <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់កាលបរិច្ឆេទ"</string>
<string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string>
<string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string>
<string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string>
@@ -1350,12 +1352,14 @@
<string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យកម្មវិធីដកសេវាកម្មនៃកម្មវិធីផ្ទុកលំនាំដើម ដើម្បីចម្លងមាតិកា។ មិនសម្រាប់ប្រើដោយកម្មវិធីលំនាំដើម។"</string>
<string name="permlab_route_media_output" msgid="1642024455750414694">"នាំផ្លូវលទ្ធផលមេឌៀ"</string>
<string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យកម្មវិធីនាំផ្លូវលទ្ធផលមេឌៀទៅឧបករណ៍ខាងក្រៅផ្សេង។"</string>
- <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string>
+ <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូលដំណើរការឧបករណ៍ផ្ទុកសុវត្ថិភាព"</string>
<string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យកម្មវិធីចូលការផ្ទុកមានសុវត្ថិភាព keguard ។"</string>
<string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យការបង្ហាញ និងលាក់ការការពារ"</string>
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យកម្មវិធីគ្រប់គ្រង keguard ។"</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"ស្ដាប់ការផ្លាស់ប្ដូរស្ថានភាពដែលទុកចិត្ត។"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"ឲ្យកម្មវិធីស្ដាប់ការផ្លាស់ប្ដូរក្នុងស្ថានភាពដែលទុកចិត្ត។"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"ផ្ដល់ភ្នាក់ងារដែលទុកចិត្ត។"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"ឲ្យកម្មវិធីផ្ដល់ភ្នាក់ងារដែលទុកចិត្ត។"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"ភ្ជាប់ទៅសេវាកម្មភ្នាក់ងារដែលទុកចិត្ត"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"ឲ្យកម្មវិធីភ្ជាប់សេវាកម្មភ្នាក់ងារដែលទុកចិត្ត។"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"អន្តរកម្មជាមួយបច្ចុប្បន្នភាព និងប្រព័ន្ធសង្គ្រោះ"</string>
@@ -1365,7 +1369,7 @@
<string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
<string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string>
<string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string>
- <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
+ <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
<string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string>
<string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string>
<string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string>
@@ -1374,7 +1378,7 @@
<string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធីមួយ ឬច្រើនដូចខាងក្រោមស្នើសិទ្ធិ ដើម្បីចូលគណនីរបស់អ្នកឥឡូវ និងពេលអនាគត។"</string>
<string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"តើអ្នកចង់អនុញ្ញាតសំណើនេះ?"</string>
<string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើចូល"</string>
- <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
+ <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
<string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
<string name="permission_request_notification_title" msgid="6486759795926237907">"បានស្នើសិទ្ធិ"</string>
<string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បានស្នើសិទ្ធិ\nសម្រាប់គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string>
@@ -1397,12 +1401,12 @@
<string name="no_file_chosen" msgid="6363648562170759465">"គ្មានឯកសារបានជ្រើស"</string>
<string name="reset" msgid="2448168080964209908">"កំណត់ឡើងវិញ"</string>
<string name="submit" msgid="1602335572089911941">"ដាក់ស្នើ"</string>
- <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string>
+ <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បានបើករបៀបរថយន្ត"</string>
<string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ ដើម្បីចេញពីរបៀបរថយន្ត។"</string>
<string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬហតស្ពតសកម្ម"</string>
<string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ ដើម្បីរៀបចំ។"</string>
<string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string>
- <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
+ <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
<string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string>
<string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការប្រើទិន្នន័យចល័តខ្ពស់"</string>
<string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ ដើម្បីស្វែងយល់បន្ថែមអំពីការប្រើទិន្នន័យចល័ត។"</string>
@@ -1428,7 +1432,7 @@
<string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍ផ្ទុកយូអេសប៊ីបច្ចុប្បន្នកំពុងប្រើដោយកុំព្យូទ័រ។"</string>
<string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្នកាតអេសឌីកំពុងប្រើដោយកុំព្យូទ័រ"</string>
<string name="media_unknown_state" msgid="729192782197290385">"មិនស្គាល់ស្ថានភាពមេឌៀខាងក្រៅ។"</string>
- <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string>
+ <string name="share" msgid="1778686618230011964">"ចែករំលែក"</string>
<string name="find" msgid="4808270900322985960">"រក"</string>
<string name="websearch" msgid="4337157977400211589">"ស្វែងរកតាមបណ្ដាញ"</string>
<string name="find_next" msgid="5742124618942193978">"រកបន្ទាប់"</string>
@@ -1444,7 +1448,7 @@
<string name="sync_undo_deletes" msgid="2941317360600338602">"មិនធ្វើការលុបវិញ"</string>
<string name="sync_do_nothing" msgid="3743764740430821845">"មិនធ្វើអ្វីទេឥឡូវ"</string>
<string name="choose_account_label" msgid="5655203089746423927">"ជ្រើសគណនី"</string>
- <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string>
+ <string name="add_account_label" msgid="2935267344849993553">"បន្ថែមគណនីថ្មី"</string>
<string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែមគណនី"</string>
<string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string>
<string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string>
@@ -1463,15 +1467,15 @@
<string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើនឆ្នាំ"</string>
<string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយឆ្នាំ"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូររបៀប"</string>
<string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
- <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើសកម្មវិធី"</string>
+ <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើសកម្មវិធី"</string>
<string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិនអាចចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</string>
+ <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែកជាមួយ"</string>
<string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែករំលែកជាមួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រងការរុញ។ ប៉ះ & សង្កត់។"</string>
<string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស ដើម្បីដោះសោ។"</string>
@@ -1485,7 +1489,7 @@
<string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍ផ្ទុកខាងក្នុង"</string>
<string name="storage_sd_card" msgid="3282948861378286745">"កាតអេសឌី"</string>
<string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍ផ្ទុកយូអេសប៊ី"</string>
- <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
+ <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
<string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមានប្រើទិន្នន័យ"</string>
<string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បីមើលការប្រើ និងការកំណត់។"</string>
<string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បានបិទទិន្នន័យ 2G-3G"</string>
@@ -1542,7 +1546,7 @@
<string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string>
<string name="media_route_status_not_available" msgid="6739899962681886401">"មិនទំនេរ"</string>
<string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុងប្រើ"</string>
- <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string>
+ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់ជាប់"</string>
<string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string>
<string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួតគ្នា"</string>
<string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
@@ -1574,7 +1578,7 @@
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string>
- <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -1683,7 +1687,7 @@
<string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
<string name="mediasize_unknown_portrait" msgid="3088043641616409762">"មិនស្គាល់បញ្ឈរ"</string>
<string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិនស្គាល់ទេសភាព"</string>
- <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string>
+ <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បានបោះបង់"</string>
<string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុសក្នុងការសរសេរមាតិកា"</string>
<string name="reason_unknown" msgid="6048913880184628119">"មិនស្គាល់"</string>
<string name="reason_service_unavailable" msgid="7824008732243903268">"មិនបានបើកសេវាកម្មបោះពុម្ព"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 111f822..2d8d0cd 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"개인"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"직장"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"개인 앱"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"업무용 Android"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"요금이 부과되는 서비스"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"요금이 부과될 수 있는 작업을 수행할 수 있도록 합니다."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"메시지"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"앱이 오디오 출력을 캡처하고 리디렉션하도록 허용합니다."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"핫워드 감지"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"앱에서 핫워드 감지를 위해 오디오를 캡처하도록 허용합니다. 캡처는 백그라운드에서 수행될 수 있지만 다른 오디오 캡처를 차단하지 않습니다(예: 캠코더)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"오디오 라우팅"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"앱이 오디오 라우팅을 직접 제어하고 오디오 정책 결정을 무시할 수 있도록 허용합니다."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"동영상 출력 캡처"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"앱이 동영상 출력을 캡처하고 리디렉션하도록 허용합니다."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"안전한 동영상 출력 캡처"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"애플리케이션에서 키가드를 제어하도록 허용합니다."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Trust 상태 변경사항 수신"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"애플리케이션이 Trust 상태에서의 변경사항을 수신할 수 있도록 허용합니다."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Trust Agent 제공"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"애플리케이션이 Trust Agent를 제공할 수 있도록 허용합니다."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Trust Agent 서비스에 연결"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"애플리케이션이 Trust Agent 서비스에 바인딩할 수 있도록 허용합니다."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"업데이트 및 복구 시스템과 상호작용"</string>
diff --git a/core/res/res/values-land/dimens_material.xml b/core/res/res/values-land/dimens_material.xml
new file mode 100644
index 0000000..c8c95d7b
--- /dev/null
+++ b/core/res/res/values-land/dimens_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+
+ <!-- Default height of an action bar. -->
+ <dimen name="action_bar_default_height_material">48dp</dimen>
+ <!-- Default padding of an action bar. -->
+ <dimen name="action_bar_default_padding_material">0dp</dimen>
+
+</resources>
diff --git a/core/res/res/values-land/dimens_quantum.xml b/core/res/res/values-land/dimens_quantum.xml
deleted file mode 100644
index 7789219..0000000
--- a/core/res/res/values-land/dimens_quantum.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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>
-
- <!-- Default height of an action bar. -->
- <dimen name="action_bar_default_height_quantum">48dp</dimen>
- <!-- Default padding of an action bar. -->
- <dimen name="action_bar_default_padding_quantum">0dp</dimen>
-
-</resources>
diff --git a/core/res/res/values-large/themes.xml b/core/res/res/values-large/themes.xml
deleted file mode 100644
index 448e7c8..0000000
--- a/core/res/res/values-large/themes.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2010, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!--
-===============================================================
- PLEASE READ
-===============================================================
-
-The Holo themes must not be modified in order to pass CTS.
-Many related themes and styles depend on other values defined in this file.
-If you would like to provide custom themes and styles for your device,
-please see themes_device_defaults.xml.
-
-===============================================================
- PLEASE READ
-===============================================================
- -->
-<resources>
- <style name="Theme.Holo.DialogWhenLarge"
- parent="@android:style/Theme.Holo.Dialog.FixedSize">
- <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
- </style>
- <style name="Theme.Holo.DialogWhenLarge.NoActionBar"
- parent="@android:style/Theme.Holo.Dialog.NoActionBar.FixedSize">
- <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
- </style>
- <style name="Theme.Holo.Light.DialogWhenLarge"
- parent="@android:style/Theme.Holo.Light.Dialog.FixedSize">
- </style>
- <style name="Theme.Holo.Light.DialogWhenLarge.NoActionBar"
- parent="@android:style/Theme.Holo.Light.Dialog.NoActionBar.FixedSize">
- </style>
-</resources>
diff --git a/core/res/res/values-large/themes_device_defaults.xml b/core/res/res/values-large/themes_device_defaults.xml
index d57e827..d252c27 100644
--- a/core/res/res/values-large/themes_device_defaults.xml
+++ b/core/res/res/values-large/themes_device_defaults.xml
@@ -31,18 +31,12 @@
===============================================================
-->
<resources>
- <style name="Theme.DeviceDefault.DialogWhenLarge"
- parent="@android:style/Theme.DeviceDefault.Dialog.FixedSize">
+ <style name="Theme.DeviceDefault.DialogWhenLarge" parent="@style/Theme.DeviceDefault.Dialog.FixedSize">
<item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
</style>
- <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar"
- parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar.FixedSize">
+ <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.Dialog.NoActionBar.FixedSize">
<item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
</style>
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge"
- parent="@android:style/Theme.DeviceDefault.Light.Dialog.FixedSize">
- </style>
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar"
- parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize">
- </style>
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="@style/Theme.DeviceDefault.Light.Dialog.FixedSize" />
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize" />
</resources>
diff --git a/core/res/res/values-large/themes_holo.xml b/core/res/res/values-large/themes_holo.xml
new file mode 100644
index 0000000..3f03932
--- /dev/null
+++ b/core/res/res/values-large/themes_holo.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Holo themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see themes_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+<resources>
+ <style name="Theme.Holo.DialogWhenLarge" parent="@style/Theme.Holo.Dialog.FixedSize">
+ <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
+ </style>
+ <style name="Theme.Holo.DialogWhenLarge.NoActionBar" parent="@style/Theme.Holo.Dialog.NoActionBar.FixedSize">
+ <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
+ </style>
+ <style name="Theme.Holo.Light.DialogWhenLarge" parent="@style/Theme.Holo.Light.Dialog.FixedSize" />
+ <style name="Theme.Holo.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Holo.Light.Dialog.NoActionBar.FixedSize" />
+</resources>
diff --git a/core/res/res/values-large/themes_material.xml b/core/res/res/values-large/themes_material.xml
new file mode 100644
index 0000000..2781608
--- /dev/null
+++ b/core/res/res/values-large/themes_material.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see themes_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+<resources>
+ <style name="Theme.Material.DialogWhenLarge" parent="@style/Theme.Material.Dialog.FixedSize">
+ <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
+ </style>
+ <style name="Theme.Material.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Dialog.NoActionBar.FixedSize">
+ <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
+ </style>
+ <style name="Theme.Material.Light.DialogWhenLarge" parent="@style/Theme.Material.Light.Dialog.FixedSize" />
+ <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Light.Dialog.NoActionBar.FixedSize" />
+</resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 71f55fb..f7ae955 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"ລະບົບ Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ສ່ວນໂຕ"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ບ່ອນເຮັດວຽກ"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"ແອັບຯສ່ວນໂຕ"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android ສຳລັບບ່ອນເຮັດວຽກ"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"ບໍລິການທີ່ເຮັດໃຫ້ທ່ານເສຍເງິນ"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"ເຮັດສິ່ງທີ່ທ່ານຕ້ອງເສຍຄ່າໃຊ້ຈ່າຍ."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"ຂໍ້ຄວາມຂອງທ່ານ"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"ອະນຸຍາດໃຫ້ແອັບຯບັນທຶກ ແລະປ່ຽນເສັ້ນທາງການປ້ອນຂໍ້ມູນອອກຂອງສຽງ."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"ການກວດຫາ Hotword"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"ອະນຸຍາດໃຫ້ແອັບຯຈັບຂໍ້ມູນສຽງສຳລັບການກວດຈັບ Hotword. ການຈັບຂໍ້ມູນສາມາດເກີດຂຶ້ນໃນພື້ນຫຼັງໄດ້ ແຕ່ຈະບໍ່ໄປຂັດຂວາງການຈັບຂໍ້ມູນສຽງອື່ນໆ (ເຊັ່ນ: ກ້ອງວິດີໂອ)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"ເສັ້ນທາງສຽງ"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"ອະນຸຍາດໃຫ້ແອັບຯຄວບຄຸມເສັ້ນທາງສຽງ ແລະປ່ຽນແປງການຕັດສິນໃຈນະໂຍບາຍສຽງໄດ້."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"ບັນທຶກວິດີໂອອອກ"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"ອະນຸຍາດໃຫ້ແອັບຯບັນທຶກ ແລະປ່ຽນເສັ້ນທາງການປ້ອນຂໍ້ມູນອອກຂອງວິດີໂອ."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"ບັນທຶກວິດີໂອອອກຢ່າງປອດໄພ"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນສາມາດຄວບຄຸມຄີກາດໄດ້."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"ຕິດຕາມການປ່ຽນແປງສະຖານະການເຊື່ອຖືກ."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນຕິດຕາມການປ່ຽນແປງໃນສະຖານະການເຊື່ອຖື."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"ລະບຸເອເຈນທີ່ເຊື່ອຖືໄດ້."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນລະບຸເອເຈນທີ່ເຊື່ອຖືໄດ້."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"ເຊື່ອມໂຍງຫາບໍລິການຕົວແທນການເຊື່ອຖື"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນເຊື່ອມໂຍງກັບບໍລິການຕົວແທນທີ່ເຊື່ອຖືໄດ້."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"ຕິດຕໍ່ກັບລະບົບອັບເດດ ແລະລະບົບກູ້ຂໍ້ມູນ."</string>
@@ -1695,7 +1699,7 @@
<string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະຈຸບັນ"</string>
<string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string>
<string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string>
- <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string>
+ <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາລັບການປັບປຸງຂໍ້ຈໍາກັດ"</string>
<string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ."</string>
<string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ສັ້ນເກີນໄປ. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ."</string>
<plurals name="restr_pin_countdown">
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 50d8239..5865662 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Saugos režimas"</string>
<string name="android_system_label" msgid="6577375335728551336">"„Android“ sistema"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Asmeninė"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Darbo"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Asmeninės programos"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Darbui skirta „Android“"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Paslaugos, už kurias mokėjote"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Atlikite mokamus veiksmus."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Jūsų pranešimai"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Programai leidžiama fiksuoti ir peradresuoti garso išvestį."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Aktyvinamųjų žodžių aptikimas"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Programai leidžiama įrašyti garsą, kad būtų galima aptikti aktyvinamuosius žodžius. Įrašymas gali būti vykdomas fone, bet tai netrikdo kitų garso įrašymo veiksmų (pvz., įrašymo vaizdo kamera)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Garso nukreipimas"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Programai leidžiama tiesiogiai valdyti garso nukreipimo ir garso nepaisymo politikos sprendimus."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"fiksuoti vaizdo išvestį"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Programai leidžiama fiksuoti ir peradresuoti vaizdo išvestį."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"fiksuoti saugią vaizdo išvestį"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Programai leidžiama valdyti „KeyGuard“."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Atsižvelgti į patikimos būsenos pakeitimus."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Programai leidžiama atsižvelgti į patikimos būsenos pakeitimus."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Teikti patikimos priemonės paslaugą."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Programai leidžiama teikti patikimos priemonės paslaugą."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Susisaistyti su „trust agent“ paslauga"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Programai leidžiama susisaistyti su „trust agent“ paslauga."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Sąveikauti su naujiniu ir atkūrimo sistema"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 5034464..8b69875 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"Pārsniedz"</string>
<string name="safeMode" msgid="2788228061547930246">"Drošais režīms"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android sistēma"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personisks"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Darba"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Personīgās lietotnes"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android darbam"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Maksas pakalpojumi"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Veikt darbības, par kurām, iespējams, būs jāmaksā."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Jūsu ziņojumi"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Ļauj lietotnei tvert un novirzīt audio izvadi."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Īsinājumvārda noteikšana"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Ļauj lietotnei tvert audio īsinājumvārda noteikšanai. Tveršana var notikt fonā, taču tā neaizkavē citu audio (piemēram, videokameras audio) tveršanu."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Audio maršrutēšana"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Ļauj lietotnei tieši kontrolēt audio maršrutēšanu un ignorēt audio politikas."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"tvert video izvadi"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Ļauj lietotnei tvert un novirzīt video izvadi."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"tvert drošu video izvadi"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Ļauj lietojumprogrammai pārvaldīt krātuvi."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Klausīties uzticamības statusa izmaiņas"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Ļauj lietojumprogrammai klausīties uzticamības statusa izmaiņas."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Nodrošināt uzticamības pārbaudes programmu"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ļauj lietojumprogrammai nodrošināt uzticamības pārbaudes programmu."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Izveidot savienojumu ar uzticamības pārbaudes pakalpojumu"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ļauj lietojumprogrammai izveidot savienojumu ar uzticamības pārbaudes pakalpojumu."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Mijiedarbošanās ar atjauninājumu un atkopšanas sistēmu"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 1177284..9f4028a 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -188,8 +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">"Андройд систем"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Хувийн"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Ажил"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Хувийн апп-ууд"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Ажилд зориулсан Андройд"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Танаас төлбөр авдаг үйлчилгээнүүд"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Таны төлбөрт оруулах зүйлийг хийх."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Таны мессеж"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Апп-т аудио гаралтыг барих, дахин чиглүүлэхийг зөвшөөрнө."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Хотворд таних"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Апп-д Хотворд илрүүлэхийн тулд аудиог бичихийг зөвшөөрнө. Бичилт далд хийгдэх бөгөөд бусад аудио бичилтэд (жнь. видео бичлэг) саад болохгүй."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Аудио чиглүүлэлт"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Апп-д аудио чиглүүлэгчийг шууд удирдах, аудио бодлогын шийдвэрүүдийг давах боломж олгоно."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"видео гаралтыг барих"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Апп-т видео гаралтыг барих, дахин чиглүүлэхийг зөвшөөрнө."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"найдвартай видео гаралтыг барих"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Аппликешн нь түлхүүр хамгаалагчыг удирдах боломжтой."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Итгэмжлэлд орж буй өөрчлөлтийг мэдэх."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Итгэмжлэлд орж буй өөрчлөлтийг мэдэх боломжийг аппликешнд олгоно."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Итгэмжлэгдсэн төлөөлөгч нийлүүлэх"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Аппликешнд итгэмжлэгдсэн төлөөлөгч нийлүүлэх боломж олгоно."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Итгэмжлэгдсэн төлөөлөгчийн үйлчилгээтэй холбогдох"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Аппликешнд итгэмжлэгдсэн төлөөлөгчтэй холбогдох боломж олгоно."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Шинэчлэлт болон сэргээх системтэй харилцах"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 16c009e..91c7c95 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Peribadi"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Tempat Kerja"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Apl peribadi"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android untuk 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>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Membenarkan apl menangkap dan mengubah hala output audio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Pengesanan sebutan laluan"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Benarkan apl merakam audio untuk pengesahan Sebutan Laluan. Rakaman ini boleh berlaku di latar belakang tetapi tidak menghalang rakaman audio lain (cth. Kamkorder)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Penghalaan Audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Membenarkan apl untuk mengawal penghalaan audio secara langsung dan mengatasi keputusan dasar audio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"tangkap output video"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Membenarkan apl menangkap dan mengubah hala output video."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"tangkap output video selamat"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Membenarkan aplikasi untuk mengawal pengawal kekunci."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Dengar perubahan keadaan amanah."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Membenarkan aplikasi mendengar perubahan dalam keadaan amanah."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Sediakan ejen amanah."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Membenarkan aplikasi menyediakan ejen amanah."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Mengikat kepada perkhidmatan ejen amanah"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Membenarkan aplikasi terikat kepada perkhidmatan ejen amanah."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Berinteraksi dengan kemas kini dan sistem pemulihan"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index acc4417..dcc648e 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Sikkermodus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personlig"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Jobb"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Personlige apper"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android for arbeidsplassen"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Betaltjenester"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Gjøre ting som kan koste deg penger."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Meldinger"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Lar appen ta opp og omdirigere lydutdata."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Gjenkjennelse av kommandoord"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Gir appen tillatelse til å ta opp lyd for å gjenkjenne kommandoord. Opptaket kan skje i bakgrunnen, men forhindrer ikke lydopptak i andre funksjoner (f.eks. i videoopptak)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Lydruting"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Tillater appen å styre lydruting direkte og overstyre angitte lydinnstillinger."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"ta opp fra videoutdata"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Lar appen ta opp og omdirigere videoutdata."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"ta opp fra sikre videoutdata"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Tillater at en app kontrollerer tastelåsen."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Oppdag endringer i tillitsstatusen."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Gir appen tillatelse til å oppdage endringer i tillitsstatusen."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Angivelse av en pålitelig agent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Tillater appen å angi en pålitelig agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Tilknytt en tillitsagent-tjeneste."</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Gir appen tillatelse til å knyttes til en tillitsagent-tjeneste."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Samhandling med oppdateringer og gjenopprettingssystem"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index d973812..4815123 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
<string name="safeMode" msgid="2788228061547930246">"Veilige modus"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-systeem"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Persoonlijk"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Werk"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Persoonlijke apps"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android voor werk"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Services waarvoor u moet betalen"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Activiteiten uitvoeren waarvoor kosten in rekening kunnen worden gebracht."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Uw berichten"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Hiermee kan de app audio-uitvoer vastleggen en verwerken."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Detectie van hotwords"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Hiermee kan de app audio opnemen voor het detecteren van hotwords. Het opnemen kan op de achtergrond plaatsvinden, maar voorkomt niet dat andere audio wordt opgenomen (bijvoorbeeld in Camcorder)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Audioroutering"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Hiermee kan de app rechtstreeks audioroutering beheren en audiobeleid negeren."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"video-uitvoer vastleggen"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Hiermee kan de app video-uitvoer vastleggen en verwerken."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"beveiligde video-uitvoer vastleggen"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Staat toe dat een app de toetsbeveiliging beheert."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Controleren op wijzigingen in de trust-status."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Toestaan dat een app controleert op wijzigingen in de trust-status."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Een trust-agent aanleveren."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Hiermee kan een app een trust-agent aanleveren."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Binden aan een trust-agentservice"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Toestaan dat een app wordt gebonden aan een trust-agentservice."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interactie met update- en herstelsysteem"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index b452018..cfbb622 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">">999"</string>
<string name="safeMode" msgid="2788228061547930246">"Tryb awaryjny"</string>
<string name="android_system_label" msgid="6577375335728551336">"System Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osobiste"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Praca"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Aplikacje osobiste"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android w pracy"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Usługi płatne"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Wykonywanie czynności, za które pobierana jest opłata."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Twoje wiadomości"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Zezwala aplikacji na przechwytywanie i przekierowywanie wyjścia audio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Wykrywanie słów-kluczy"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Umożliwia aplikacji przechwytywanie dźwięku w celu wykrywania słów-kluczy. Może się to odbywać w tle i nie uniemożliwia innego przechwytywania dźwięku (np. z kamery)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Kierowanie dźwiękowe"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Zezwala aplikacji na bezpośrednie sterowanie kierowaniem dźwiękowym i zastępowanie decyzji zasad dźwięku."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"przechwyć wyjście wideo"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Zezwala aplikacji na przechwytywanie i przekierowywanie wyjścia wideo."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"przechwyć bezpieczne wyjście wideo"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Umożliwia aplikacji kontrolowanie zabezpieczenia kluczami."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Monitoruj zmiany w stanie zaufania."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Zezwala aplikacji na monitorowanie zmian w stanie zaufania."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Dostarczaj agenta zaufania."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Zezwala aplikacji na dostarczanie agenta zaufania."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Powiąż z usługą agenta zaufania"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Zezwala aplikacji na powiązanie z usługą agenta zaufania."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakcja z systemem odzyskiwania i aktualizacjami"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index d3f5a63..0ddc2d3 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Modo seguro"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistema Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Aplicações pessoais"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android para trabalho"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Serviços que implicam pagamento"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Efetuar ações que implicam pagamento."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"As suas mensagens"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Permite à aplicação capturar e redirecionar a saída de áudio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Deteção de palavra de ativação"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Permite à aplicação capturar áudio para deteção da palavra de ativação. A captura pode acontecer em segundo plano, mas não impede outras capturas de áudio (por exemplo com câmara de vídeo)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Direcionamento do áudio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Permite que a aplicação controle diretamente o direcionamento do áudio e substitua as decisões da política de áudio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"capturar saída de vídeo"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Permite à aplicação capturar e redirecionar a saída de vídeo."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"capturar saída de vídeo segura"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite que uma aplicação controle a proteção de teclado."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Registar alterações no estado trust."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que uma aplicação registe alterações no trust state."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fornecer um agente fidedigno."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que uma aplicação forneça um agente fidedigno."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Vincular a um serviço de trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que uma aplicação fique vinculada a um serviço de trust agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir com o sistema de recuperação e de atualização"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index ae15d48..1e36eaf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Pessoal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabalho"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Aplicativos pessoais"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android para o 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>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Permite que o aplicativo capture e redirecione a saída de áudio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Detecção de hotwords"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Permite que o dispositivo capture áudio para a detecção de hotwords. A captura pode acontecer em segundo plano, mas não impede outras capturas de áudio (como por uma câmera de vídeo)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Roteamento de áudio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Permite que o app controle diretamente o roteamento de áudio e substitua as decisões relacionadas a políticas de áudio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"capturar saída de vídeo"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Permite que o aplicativo capture e redirecione a saída de vídeo."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"capturar saída de vídeo segura"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite que o aplicativo controle o bloqueio de teclado."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Detectar alterações no estado de confiança."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que o aplicativo detecte alterações no estado de confiança."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fornecer um agente de confiança."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que um aplicativo forneça um agente de confiança."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Associar a um serviço de agente de confiança"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que o aplicativo se associe a um serviço de agente de confiança."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagir com o sistema de atualizações e recuperação"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index a3cf563..9ba2fbe 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -246,9 +246,9 @@
<skip />
<string name="safeMode" msgid="2788228061547930246">"Modus segirà"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
- <!-- no translation found for user_owner_label (2804351898001038951) -->
+ <!-- no translation found for user_owner_label (6465364741001216388) -->
<skip />
- <!-- no translation found for managed_profile_label (6260850669674791528) -->
+ <!-- no translation found for managed_profile_label (3022906847647343112) -->
<skip />
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Servetschs che custan"</string>
<!-- no translation found for permgroupdesc_costMoney (3293301903409869495) -->
@@ -863,6 +863,10 @@
<skip />
<!-- no translation found for permdesc_captureAudioHotword (9151807958153056810) -->
<skip />
+ <!-- no translation found for permlab_modifyAudioRouting (7738060354490807723) -->
+ <skip />
+ <!-- no translation found for permdesc_modifyAudioRouting (7205731074267199735) -->
+ <skip />
<!-- no translation found for permlab_captureVideoOutput (2246828773589094023) -->
<skip />
<!-- no translation found for permdesc_captureVideoOutput (359481658034149860) -->
@@ -2178,6 +2182,10 @@
<skip />
<!-- no translation found for permdesc_trust_listener (8233895334214716864) -->
<skip />
+ <!-- no translation found for permlab_provide_trust_agent (5465587586091358316) -->
+ <skip />
+ <!-- no translation found for permdesc_provide_trust_agent (3865702641053068148) -->
+ <skip />
<!-- no translation found for permlab_bind_trust_agent_service (8242093169457695334) -->
<skip />
<!-- no translation found for permdesc_bind_trust_agent_service (7041930026024507515) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 03b7d40..8ab8ccf 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Serviciu"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Aplicații personale"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android pentru 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>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Permite aplicației să intercepteze și să redirecționeze ieșirea audio."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"detectarea expresiei de activare"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Activează captarea semnalului audio de către aplicație pentru detectarea expresiei de activare. Captarea poate avea loc în fundal, dar nu împiedică altă captare audio (de ex., cameră video)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Cale audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Permite aplicației să controleze în mod direct calea audio și să suprascrie deciziile privind politicile audio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"să intercepteze ieșirea video"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Permite aplicației să intercepteze și să redirecționeze ieșirea video."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"să intercepteze ieșirea video securizată"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Permite unei aplicații să controleze blocarea tastaturii."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Detectarea modificărilor în starea de încredere."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite unei aplicații să detecteze modificările în starea de încredere."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Indicați un agent de încredere."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite unei aplicații să indice un agent de încredere."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Asocierea la un serviciu „agenți de încredere”."</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite unei aplicații să se asocieze la un serviciu „agent de încredere”."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interacțiune cu sistemul de recuperare și de actualizare"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index bb98508..17a0396 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -176,7 +176,7 @@
<string name="global_action_lock" msgid="2844945191792119712">"Блокировка экрана"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Отключить питание"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Отчет об ошибке"</string>
- <string name="bugreport_title" msgid="2667494803742548533">"Создание отчета об ошибке"</string>
+ <string name="bugreport_title" msgid="2667494803742548533">"Отчет об ошибке"</string>
<string name="bugreport_message" msgid="398447048750350456">"Информация о текущем состоянии вашего устройства будет собрана и отправлена по электронной почте. Подготовка отчета займет некоторое время."</string>
<string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Режим без звука"</string>
<string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Выключить"</string>
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Личные данные"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Работа"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Персональные приложения"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android для бизнеса"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Платные услуги"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Приложение сможет использовать платные услуги."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Сообщения"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Приложение сможет захватывать и перенаправлять аудиосигнал."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"распознавать голосовые команды"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Приложение сможет анализировать звук для распознавания голосовых команд. Этот процесс выполняется в фоновом режиме и не мешает другим операциям (например, записи видеоролика)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Маршрутизация аудио"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Управление маршрутизацией аудио и перезапись правил, связанных с аудио."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"захват видеосигнала"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Приложение сможет захватывать и перенаправлять видеосигнал."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"захват защищенного видеосигнала"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Приложение сможет управлять хранилищем ключей."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Отслеживание изменений статуса доверия"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Приложение сможет отслеживать изменения в статусе доверия."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Доверенный агент"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Предоставление доверенных агентов."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Подключение к службе Trust Agents"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Приложение сможет подключаться к службе Trust Agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Взаимодействовать с системой восстановления и обновлениями"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 7e8019a..e98a4be 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Núdzový režim"</string>
<string name="android_system_label" msgid="6577375335728551336">"Systém Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osobné"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Práca"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Osobné aplikácie"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android na prácu"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Spoplatnené služby"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Vykonávanie činností, ktoré vás môžu stáť peniaze."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vaše správy"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Umožňuje aplikácii zachytiť a presmerovať výstup zvuku."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Rozpoznanie kľúčových slov"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Umožňuje aplikácii zaznamenať zvuk s cieľom rozpoznať kľúčové slová. Záznam sa môže uskutočniť na pozadí a nebráni inému zaznamenávaniu zvuku (napríklad videokamerou)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Smerovanie zvuku"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Umožňuje aplikácii priamo ovládať smerovanie zvuku a prepísať rozhodnutia týkajúce sa pravidiel pre zvuk."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"zachytiť výstup videa"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Umožňuje aplikácii zachytiť a presmerovať výstup videa."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"zachytiť zabezpečený výstup videa"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Umožňuje aplikácii ovládať technológiu keyguard."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Reagovanie na zmeny stavu dôveryhodnosti."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Umožňuje aplikácii reagovať na zmeny stavu dôveryhodnosti."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Poskytnúť dôveryhodného agenta"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Umožňuje aplikácii poskytnúť dôveryhodného agenta."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Viazanie sa na službu zástupcu dôveryhodnosti"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Umožňuje aplikácii viazať sa na službu zástupcu dôveryhodnosti."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interakcia so systémom aktualizácií a obnovenia"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index e681737..280e499 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999 +"</string>
<string name="safeMode" msgid="2788228061547930246">"Varni način"</string>
<string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Osebno"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Služba"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Osebne aplikacije"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android za delo"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Plačljive storitve"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Dovolite stvari, za katere bo morda treba plačati."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Vaša sporočila"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Omogoči aplikaciji, da zajame in preusmeri avdioizhod."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Zaznavanje sprožilnih besed"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Aplikaciji dovoljuje snemanje zvoka za zaznavanje sprožilnih besed. Snemanje je možno tudi v ozadju, ne preprečuje pa drugega snemanja zvoka (npr. z videokamero)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Usmerjanje zvoka"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Aplikaciji dovoli neposredno nadziranje usmerjanja zvoka in preglasitev odločitev pravilnika za zvok."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"zajem videoizhoda"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Omogoči aplikaciji, da zajame in preusmeri videoizhod."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"zajem varnega videoizhoda"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Aplikaciji omogoča nadzor zaklepanja tipkovnice."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Spremljanje sprememb stanja zaupanja."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Aplikaciji dovoli spremljanje sprememb stanja zaupanja."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Navedba posrednika zaupanja."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Aplikaciji dovoli navesti posrednika zaupanja."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Povezovanje s storitvijo posrednikov zaupanja"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Aplikaciji dovoli povezovanje s storitvijo posrednikov zaupanja."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Uporaba sistema za posodobitev in obnovitev"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 51a7839..6147fd8 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Лично"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Посао"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Личне апликације"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android за посао"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Услуге које се плаћају"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Покреће радње које могу да се плаћају."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Поруке"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Дозвољава апликацији да снима и преусмерава аудио садржај."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Откривање актуелних речи"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Дозвољава апликацији да снима звук за откривање актуелних речи. Снимање може да се дешава у позадини, али не спречава друга снимања звука (нпр. камкордер)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Преусмеравање звука"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Дозвољава апликацији да директно контролише преусмеравање звука и замени одлуке смерница о звуку."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"снимање видео садржаја"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Дозвољава апликацији да снима и преусмерава видео садржај."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"снимање безбедног видео садржаја"</string>
@@ -608,7 +610,7 @@
<string name="permlab_setWallpaperHints" msgid="3278608165977736538">"прилагођавање величине позадине"</string>
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"Дозвољава апликацији да подеси савете за системску величину позадине."</string>
<string name="permlab_masterClear" msgid="2315750423139697397">"поновно постављање фабричких подразумеваних подешавања"</string>
- <string name="permdesc_masterClear" msgid="3665380492633910226">"Дозвољава апликацији да поново постави комплетна фабричка подешавања система и тиме избрише све податке, конфигурацију и инсталиране апликације."</string>
+ <string name="permdesc_masterClear" msgid="3665380492633910226">"Дозвољава апликацији да ресетује систем на фабричка подешавања и тиме избрише све податке, конфигурацију и инсталиране апликације."</string>
<string name="permlab_setTime" msgid="2021614829591775646">"подешавање времена"</string>
<string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"Дозвољава апликацији да промени време на сату таблета."</string>
<string name="permdesc_setTime" product="default" msgid="1855702730738020">"Дозвољава апликацији да промени време на сату телефона."</string>
@@ -732,8 +734,8 @@
<string name="policylab_forceLock" msgid="2274085384704248431">"Закључавање екрана"</string>
<string name="policydesc_forceLock" msgid="1141797588403827138">"Контролишите начин и време закључавања екрана."</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"Брисање свих података"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Брисање података на таблету без упозорења враћањем фабричких података."</string>
- <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Избришите податке на телефону без упозорења враћањем фабричких података."</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Брисање података на таблету без упозорења ресетовањем на фабричка подешавања."</string>
+ <string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Избришите податке на телефону без упозорења ресетовањем на фабричка подешавања."</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Подесите глобални прокси сервер уређаја"</string>
<string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Подесите глобални прокси сервер уређаја који ће се користити док су омогућене смернице. Само први администратор уређаја поставља ефективни глобални прокси сервер."</string>
<string name="policylab_expirePassword" msgid="885279151847254056">"Подешавање истека лозинке екрана"</string>
@@ -906,8 +908,8 @@
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"<xliff:g id="NUMBER_0">%d</xliff:g> пута сте погрешно унели PIN. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"<xliff:g id="NUMBER_0">%d</xliff:g> пута сте нетачно унели шаблон за откључавање. Након још <xliff:g id="NUMBER_1">%d</xliff:g> несупешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу података за пријављивање на Google.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"<xliff:g id="NUMBER_0">%d</xliff:g> пута сте нетачно унели шаблон за откључавање. Након још <xliff:g id="NUMBER_1">%d</xliff:g> несупешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу података за пријављивање на Google.\n\n Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Неправилно сте покушали да откључате таблет <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%d</xliff:g>) таблет ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Неисправно сте покушали да откључате телефон <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%d</xliff:g>) телефон ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Неправилно сте покушали да откључате таблет <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%d</xliff:g>) таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Неисправно сте покушали да откључате телефон <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још неуспешних покушаја (<xliff:g id="NUMBER_1">%d</xliff:g>) телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Неисправно сте покушали да откључате таблет <xliff:g id="NUMBER">%d</xliff:g> пута. Таблет ће сада бити враћен на подразумевана фабричка подешавања."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Неисправно сте покушали да откључате телефон <xliff:g id="NUMBER">%d</xliff:g> пута. Телефон ће сада бити враћен на подразумевана фабричка подешавања."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Покушајте поново за <xliff:g id="NUMBER">%d</xliff:g> секунде(и)."</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Дозвољава апликацији да контролише заштиту шифром."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Праћење промена Trust стања."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Дозвољава апликацији да прати промене Trust стања."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Обезбеђивање поузданог агента."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Дозвољава апликацији да обезбеди поузданог агента."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Везивање за услугу Trust agents"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Дозвољава апликацији да се веже за услугу Trust agents."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Интеракција са системом за ажурирање и опоравак"</string>
@@ -1582,8 +1586,8 @@
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Унели сте PIN неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Унели сте лозинку неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја таблет ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја телефон ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER">%d</xliff:g> пута. Таблет ће сада бити враћен на подразумевана фабричка подешавања."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER">%d</xliff:g> пута. Телефон ће сада бити враћен на подразумевана фабричка подешавања."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу налога е-поште.\n\nПокушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 595dadd..acd5bee 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Säkert läge"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android-system"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personligt"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Arbetet"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Personliga appar"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android för arbetet"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Tjänster som kostar pengar"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Göra saker som kan kosta pengar."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Dina meddelanden"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Tillåt att appen fångar upp och omdirigerar ljudutgången."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Kommandoordsidentifiering"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Tillåter att appen spelar in ljud för att upptäcka kommandoord. Inspelningen kan pågå i bakgrunden utan att hindra andra ljudinspelningar (t.ex. med videokamera)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Ljuddirigering"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Tillåter att appen styr ljuddirigeringen direkt och åsidosätter ljudpolicybeslut."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"fånga upp videoutgång"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Tillåt att appen fångar upp och omdirigerar videoutgången."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"fånga upp säker videoutgång"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Tillåter att en app kontrollerar knapplåsfunktionen."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Lyssna efter ändringar i betrodda agenters status."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Tillåter att en app lyssnar efter ändringar i den betrodda agentens status."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Tillhandahåll en betrodd agent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Tillåter att en app tillhandahåller en betrodd agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bind till en tjänst från en betrodd agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Tillåter att en app binds vid en tjänst från en betrodd agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Interagera med uppdaterings- och återställningssystemet"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index bb39100..b177516 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Mtindo salama"</string>
<string name="android_system_label" msgid="6577375335728551336">"Mfumo wa Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Binafsi"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Kazini"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Programu binafsi"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android kwa Kazi"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Huduma ambazo zinakugharimu pesa"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Fanya mambo ambayo yanaweza kukugharimu pesa."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Ujumbe wako"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Inaruhusu programu kunasa na kuelekeza sauti kwingine."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Kutambua neno tekelezi"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Inaruhusu programu kunasa sauti kwa ajili ya utambuzi wa Neno Tekelezi. Kunasa kunaweza kukafanyika chinichini lakini hakutazuia unasaji mwingine wa sauti (kwa mfano Kamkoda)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Uelekezaji wa Sauti"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Huruhusu programu kudhibiti uelekezaji wa sauti moja kwa moja na kupuuza maamuzi ya sera ya sauti."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"nasa sauti ya video"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Inaruhusu programu kunasa na kuelekeza video kwingine."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"nasa sauti ya video kwa usalama"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Huruhusu programu kudhibiti kilinda-funguo."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Sikiliza mabadiliko ya hali ya kuaminiwa."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Huruhusu programu kusikiliza mabadiliko katika hali ya kuaminiwa."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Toa wakala wa uaminifu."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Huruhusu programu kutoa wakala wa uaminifu."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Funga kwenye huduma ya dalali wa kuaminiwa"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Huruhusu programu kufungamanisha kwenye huduma ya dalali wa kuaminiwa."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Ingiliana na sasisho na mfumo wa kurejesha"</string>
diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml
index 6e17cdd3..f92ff40 100644
--- a/core/res/res/values-television/themes.xml
+++ b/core/res/res/values-television/themes.xml
@@ -18,6 +18,6 @@
<style name="Theme.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
<style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
- <style name="Theme.Quantum.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
- <style name="Theme.Quantum.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+ <style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
+ <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index d235eaa..c5ef006 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -188,8 +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">"ระบบแอนดรอยด์"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"ส่วนตัว"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"ที่ทำงาน"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"แอปส่วนตัว"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"แอนดรอยด์สำหรับการทำงาน"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"บริการที่ต้องเสียค่าใช้จ่าย"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"ทำสิ่งที่คุณต้องเสียค่าใช้จ่าย"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"ข้อความของคุณ"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"อนุญาตให้แอปบันทึกและเปลี่ยนเส้นทางเอาต์พุตเสียง"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"การตรวจหาคำที่นิยม"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"อนุญาตให้แอปเก็บเสียงสำหรับการตรวจหาคำที่นิยม การเก็บเสียงสามารถดำเนินการอยู่ในพื้นหลัง แต่ไม่เป็นการป้องกันการเก็บเสียงอื่นๆ (เช่น กล้องวิดีโอ)"</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"การกำหนดเส้นทางเสียง"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"ช่วยให้แอปสามารถควบคุมการกำหนดเส้นทางเสียงได้โดยตรง และแทนที่การตัดสินใจในนโยบายด้านเสียง"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"บันทึกเอาต์พุตวิดีโอ"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"อนุญาตให้แอปบันทึกและเปลี่ยนเส้นทางเอาต์พุตวิดีโอ"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"บันทึกเอาต์พุตเสียงที่ปลอดภัย"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"อนุญาตให้แอปพลิเคชันควบคุมตัวล็อกปุ่มกด"</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"ฟังการเปลี่ยนแปลงของสถานะความน่าเชื่อถือ"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"อนุญาตให้แอปพลิเคชันฟังการเปลี่ยนแปลงที่มีต่อสถานะความน่าเชื่อถือ"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"เสนอตัวแทนที่เชื่อถือได้"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"ช่วยให้แอปพลิเคชันสามารถเสนอตัวแทนที่เชื่อถือได้"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"ผูกกับบริการของตัวแทนที่เชื่อถือได้"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"อนุญาตให้แอปพลิเคชันผูกกับบริการของตัวแทนที่เชื่อถือได้"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"โต้ตอบกับการอัปเดตและระบบการกู้คืน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 2b7d961..84e83d3 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Safe mode"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android System"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Personal"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Trabaho"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Mga personal na app"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android para sa Trabaho"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Mga serbisyong ginagastusan mo"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Gumawa ng mga bagay na magpapagastos sa iyo."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Iyong mga mensahe"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Pinapayagan ang app na kumuha at mag-redirect ng audio output."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Paghahanap ng hotword"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Nagbibigay-daan sa app na kumuha ng audio na paghahanapan ng Hotword. Maaaring maisagawa sa background ang pagkuha, ngunit hindi nito pipigilan ang iba pang pagkuha ng audio (hal. Camcorder)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Pagruruta ng Audio"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Pinapayagan ang app na direktang kontrolin ang pagruruta ng audio at i-override ang mga pasya sa patakaran sa audio."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"kumuha ng video output"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Pinapayagan ang app na kumuha at mag-redirect ng video output."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"kumuha ng secure na video output"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Pinapayagan ang isang application na kontrolin ang keyguard."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Makinig sa mga pagbabago sa estado ng trust."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Pinapayagan ang isang application na makinig para sa mga pagbabago sa estado ng trust."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Magbigay ng trust agent."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Pinapayagan ang isang application na magbigay ng trust agent."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Sumailalim sa isang serbisyo ng trust agent"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Pinapayagan ang isang application na sumailalim sa isang serbisyo ng trust agent."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Makipag-ugnay sa system ng pag-update at pagbawi"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index d5e227e..04bec96 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Güvenli mod"</string>
<string name="android_system_label" msgid="6577375335728551336">"Android Sistemi"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Kişisel"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"İş"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Kişisel uygulamalar"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"İş için Android"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Size maliyet getiren hizmetler"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Size maliyet getirebilecek işlemler yapma."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Mesajlarınız"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Uygulamaya, ses çıkışını yakalayıp yönlendirme izni verir."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Özel kelime algılama"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Uygulamaya, Özel Kelime algılamak için ses yakalama izni verir. Ses yakalama işlemi arka planda yapılabilir, ancak diğer ses yakalama işlemlerini (ör. kameranın ses kaydını) engellemez."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Ses Yönlendirme"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Uygulamanın ses yönlendirmeyi doğrudan denetlemesine ve ses politikası kararlarını geçersiz kılmasına izin verir."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"video çıkışını yakala"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Uygulamaya, video çıkışını yakalayıp yönlendirme izni verir."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"güvenli video çıkışını yakala"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Bir uygulamaya tuş koruyucuyu denetleme izni verir."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Güven durumundaki değişiklileri dinle."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Bir uygulamanın, güven durumundaki değişiklikleri dinlemesine izin verir."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Güven aracısı sağlama."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Bir uygulamanın güven aracısı sağlamasına izin verir."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Güven aracı hizmetine bağlan"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Bir uygulamanın, güven aracı hizmetine bağlanmasına izin verir."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Güncelleme ve kurtarma sistemiyle etkileşim kur"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 5088a8a..648d625 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"Особистий профіль"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Службовий профіль"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Особисті додатки"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android для роботи"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Служби, які потребують оплати"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Виконувати дії, які потребують оплати."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Ваші повідомл."</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Дозволяє програмі отримувати доступ до аудіовиходу й переспрямовувати його."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"виявляти команди швидкого запуску"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Дозволяє програмі записувати аудіо для виявлення команд швидкого запуску. Запис відбуватиметься у фоновому режимі й не перешкоджатиме запису іншого аудіо (напр., з відеокамери)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Маршрутизація аудіо"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Додаток може безпосередньо керувати маршрутизацією аудіо та змінювати правила керування аудіо."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"отримувати доступ до відеовиходу"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Дозволяє програмі отримувати доступ до відеовиходу й переспрямовувати його."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"отримувати доступ до захищеного відеовиходу"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Дозволяє програмі керувати клавіатурою."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Відстежувати зміни в стані довіри."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Дозволяє додатку відстежувати зміни в стані довіри."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Призначення довірчого агента."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Додаток може призначати довірчого агента."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Прив’язуватися до служби довірчих агентів"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Дозволяє додатку прив’язуватися до служби довірчих агентів."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Взаємодіяти з оновленнями системи та системою відновлення."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 7961f84..7ee9070 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Chế độ an toàn"</string>
<string name="android_system_label" msgid="6577375335728551336">"Hệ thống Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Cá nhân"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Cơ quan"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Ứng dụng cá nhân"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android dành cho công việc"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Dịch vụ tính tiền của bạn"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Thực hiện những tác vụ mà bạn có thể phải trả tiền."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Tin nhắn của bạn"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Cho phép ứng dụng thu thập và chuyển hướng dữ liệu đầu ra âm thanh."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Phát hiện từ nóng"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Cho phép ứng dụng thu thập dữ liệu âm thanh để phát hiện từ nóng. Quá trình thu thập này có thể diễn ra trong nền nhưng không ngăn các hoạt động thu thập dữ liệu âm thanh khác (ví dụ: máy quay video)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Định tuyến âm thanh"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Cho phép ứng dụng trực tiếp kiểm soát định tuyến âm thanh và ghi đè các quyết định về chính sách âm thanh."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"thu thập dữ liệu đầu ra video"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Cho phép ứng dụng thu thập và chuyển hướng dữ liệu đầu ra video."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"thu thập dữ liệu đầu ra video an toàn"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Cho phép ứng dụng kiểm soát tính năng bảo vệ phím."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Quan sát các thay đổi ở trạng thái đáng tin cậy."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Cho phép ứng dụng quan sát các thay đổi ở trạng thái đáng tin cậy."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Cung cấp tác nhân đáng tin cậy."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Cho phép ứng dụng cung cấp tác nhân đáng tin cậy."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Liên kết với một dịch vụ của đại lý đáng tin cậy"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Cho phép ứng dụng liên kết với một dịch vụ của đại lý đáng tin cậy."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Tương tác với hệ thống khôi phục và bản cập nhật"</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 8d82a17..6052fb0 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -36,4 +36,8 @@
<!-- Maximum velocity to initiate a fling, as measured in dips per second. -->
<dimen name="config_viewMaxFlingVelocity">8000dp</dimen>
+ <!-- Number of notifications to keep in the notification service historical archive.
+ Reduced intentionally for watches to retain minimal memory footprint -->
+ <integer name="config_notificationServiceArchiveSize">1</integer>
+
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 5771bd9..d37eddd 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"个人"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"企业"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"个人应用"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android for Work"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"需要您付费的服务"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"执行可能需要您付费的操作。"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"您的信息"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"允许该应用捕获和重定向音频输出。"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"启动指令检测"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"允许应用捕获音频以便检测语音启动指令。捕获操作会在后台进行,但不会妨碍其他音频捕获工具(例如摄像机)。"</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"音频路由"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"允许应用直接控制音频路由以及覆盖音频政策决策。"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"捕获视频输出"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"允许该应用捕获和重定向视频输出。"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"捕获安全视频输出"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"允许应用控制锁屏。"</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"检测信任状态的变化。"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"允许应用检测信任状态的变化。"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"提供信任的代理。"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"允许应用提供信任的代理。"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"绑定至信任的代理服务"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"允许应用绑定至信任的代理服务。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"与更新和恢复系统互动"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index cd0fe61..a9e1770 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"個人"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"公司"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"個人應用程式"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android 企業版"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"付費服務"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"執行需付費的操作或服務。"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"您的訊息"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"允許應用程式擷取及重新導向音頻輸出。"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"啟動字詞偵測"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"允許應用程式擷取啟動字詞偵測的音頻。擷取操作可以在背景執行,但並未阻止其他音頻擷取 (例如攝錄機)。"</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"音效檔案路由"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"允許應用程式直接控制音效檔案路由及覆寫音效檔案政策決定。"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"擷取視頻輸出"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"允許應用程式擷取及重新導向視頻輸出。"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"擷取安全視頻輸出"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"允許應用程式控制鍵盤鎖。"</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"聽取信任狀態變更。"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"允許應用程式聽取信任狀態的變更。"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"提供信任的代理程式。"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"允許應用程式提供信任的代理程式。"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"繫結至信任的代理程式服務"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"允許應用程式繫結至信任的代理程式服務。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"與更新和復原系統互動"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index aac387a..b1aaae7 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -188,8 +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>
- <string name="user_owner_label" msgid="2804351898001038951">"個人"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"公司"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"個人應用程式"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"Android 企業版"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"需要額外費用的服務。"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"執行需付費的作業或服務。"</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"您的簡訊"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"允許應用程式擷取及重新導向音訊輸出。"</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"啟動字詞偵測"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"允許應用程式擷取音訊用於啟動字詞偵測。擷取作業可在背景執行,但並未禁止使用其他音訊擷取工具 (例如攝錄影機)。"</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"音訊路由"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"允許應用程式直接控制音訊路由及覆寫音訊政策決定。"</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"擷取視訊輸出"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"允許應用程式擷取及重新導向視訊輸出。"</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"擷取安全視訊輸出"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"允許應用程式控制鍵盤鎖。"</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"接聽信任狀態變更。"</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"允許應用程式接聽信任狀態變更。"</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"提供信任的代理程式。"</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"允許應用程式提供信任的代理程式。"</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"繫結至信任的代理程式服務"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"允許應用程式繫結至信任的代理程式服務。"</string>
<string name="permlab_recovery" msgid="3157024487744125846">"與更新和還原系統互動"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ef25010..25ff769 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -188,8 +188,8 @@
<string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
<string name="safeMode" msgid="2788228061547930246">"Imodi ephephile"</string>
<string name="android_system_label" msgid="6577375335728551336">"Uhlelo lwe-Android"</string>
- <string name="user_owner_label" msgid="2804351898001038951">"Okomuntu siqu"</string>
- <string name="managed_profile_label" msgid="6260850669674791528">"Umsebenzi"</string>
+ <string name="user_owner_label" msgid="6465364741001216388">"Izinhlelo zokusebenza zomuntu siqu"</string>
+ <string name="managed_profile_label" msgid="3022906847647343112">"I-Android yomsebenzi"</string>
<string name="permgrouplab_costMoney" msgid="5429808217861460401">"Amasevisi abiza imali"</string>
<string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Yenza izinto ezingakudla imali."</string>
<string name="permgrouplab_messages" msgid="7521249148445456662">"Imiyalezo yakho"</string>
@@ -520,6 +520,8 @@
<string name="permdesc_captureAudioOutput" msgid="6210597754212208853">"Kuvumela uhlelo lokusebenza ukuba lushuthe futhi luqondise kabusha okukhipha umsindo."</string>
<string name="permlab_captureAudioHotword" msgid="1890553935650349808">"Ukutholwa kwe-Hotword"</string>
<string name="permdesc_captureAudioHotword" msgid="9151807958153056810">"Ivumela uhlelo lokusebenza ukuthi lishuthele umsindo ukutholwa kwe-Hotword. Ukushutha kungenzeka ngemuva kodwa akuvimbeli okunye ukushutha komsindo (isb. i-Camcorder)."</string>
+ <string name="permlab_modifyAudioRouting" msgid="7738060354490807723">"Umazila womsindo"</string>
+ <string name="permdesc_modifyAudioRouting" msgid="7205731074267199735">"Ivumela uhlelo lokusebenza ukulawla ngqo umzila womsindo nokubhala ngaphezulu izinqumo zenqubomgomo zomsindo."</string>
<string name="permlab_captureVideoOutput" msgid="2246828773589094023">"shutha okokukhipha ividiyo"</string>
<string name="permdesc_captureVideoOutput" msgid="359481658034149860">"Kuvumela uhlelo lokusebenza ukuba lushuthe futhi luqondise kabusha okukhipha ividiyo."</string>
<string name="permlab_captureSecureVideoOutput" msgid="7815398969303382016">"shutha okukhipha ividiyo ephephile"</string>
@@ -1356,6 +1358,8 @@
<string name="permdesc_control_keyguard" msgid="3043732290518629061">"Ivumela uhlelo lokusebenza ukuthi lulawule ukhiye wokuqapha."</string>
<string name="permlab_trust_listener" msgid="1765718054003704476">"Lalela izinguquko zesimo sokuthemba."</string>
<string name="permdesc_trust_listener" msgid="8233895334214716864">"Ivumela uhlelo lokusebenza ukuthi lilalelele izinguquko kusimo sethemba."</string>
+ <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Nikeza umsebenzeli owethembekile."</string>
+ <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ivumela uhlelo lokusebenza ukunikeza umsebenzeli owethembekile."</string>
<string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bophezela kusevisi yomenzeli wethemba"</string>
<string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ivumela uhlelo lokusebenza ukuthi libophezeleke kusevisi yomenzeli wethemba."</string>
<string name="permlab_recovery" msgid="3157024487744125846">"Ixhumana nesibuyekezo nesistimu yokutakula"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index f01f10e..c3036d5 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -298,6 +298,189 @@
<item>@drawable/quickcontact_badge_overlay_light</item>
<item>@drawable/quickcontact_badge_overlay_normal_light</item>
<item>@drawable/quickcontact_badge_overlay_pressed_light</item>
+
+ <!-- Material assets -->
+ <item>@drawable/ab_share_pack_mtrl_alpha</item>
+ <item>@drawable/ab_solid_shadow_mtrl_alpha</item>
+ <item>@drawable/btn_cab_done_mtrl_alpha</item>
+ <item>@drawable/btn_check_to_off_mtrl_000</item>
+ <item>@drawable/btn_check_to_off_mtrl_001</item>
+ <item>@drawable/btn_check_to_off_mtrl_002</item>
+ <item>@drawable/btn_check_to_off_mtrl_003</item>
+ <item>@drawable/btn_check_to_off_mtrl_004</item>
+ <item>@drawable/btn_check_to_off_mtrl_005</item>
+ <item>@drawable/btn_check_to_off_mtrl_006</item>
+ <item>@drawable/btn_check_to_off_mtrl_007</item>
+ <item>@drawable/btn_check_to_off_mtrl_008</item>
+ <item>@drawable/btn_check_to_off_mtrl_009</item>
+ <item>@drawable/btn_check_to_off_mtrl_010</item>
+ <item>@drawable/btn_check_to_off_mtrl_011</item>
+ <item>@drawable/btn_check_to_off_mtrl_012</item>
+ <item>@drawable/btn_check_to_off_mtrl_013</item>
+ <item>@drawable/btn_check_to_off_mtrl_014</item>
+ <item>@drawable/btn_check_to_off_mtrl_015</item>
+ <item>@drawable/btn_check_to_on_mtrl_000</item>
+ <item>@drawable/btn_check_to_on_mtrl_001</item>
+ <item>@drawable/btn_check_to_on_mtrl_002</item>
+ <item>@drawable/btn_check_to_on_mtrl_003</item>
+ <item>@drawable/btn_check_to_on_mtrl_004</item>
+ <item>@drawable/btn_check_to_on_mtrl_005</item>
+ <item>@drawable/btn_check_to_on_mtrl_006</item>
+ <item>@drawable/btn_check_to_on_mtrl_007</item>
+ <item>@drawable/btn_check_to_on_mtrl_008</item>
+ <item>@drawable/btn_check_to_on_mtrl_009</item>
+ <item>@drawable/btn_check_to_on_mtrl_010</item>
+ <item>@drawable/btn_check_to_on_mtrl_011</item>
+ <item>@drawable/btn_check_to_on_mtrl_012</item>
+ <item>@drawable/btn_check_to_on_mtrl_013</item>
+ <item>@drawable/btn_check_to_on_mtrl_014</item>
+ <item>@drawable/btn_check_to_on_mtrl_015</item>
+ <item>@drawable/btn_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00000_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00001_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00002_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00003_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00004_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00005_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00006_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00007_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00008_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00009_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00010_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00011_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00012_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00013_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00014_mtrl_alpha</item>
+ <item>@drawable/btn_radio_anim_00015_mtrl_alpha</item>
+ <item>@drawable/btn_radio_to_off_mtrl_000</item>
+ <item>@drawable/btn_radio_to_off_mtrl_001</item>
+ <item>@drawable/btn_radio_to_off_mtrl_002</item>
+ <item>@drawable/btn_radio_to_off_mtrl_003</item>
+ <item>@drawable/btn_radio_to_off_mtrl_004</item>
+ <item>@drawable/btn_radio_to_off_mtrl_005</item>
+ <item>@drawable/btn_radio_to_off_mtrl_006</item>
+ <item>@drawable/btn_radio_to_off_mtrl_007</item>
+ <item>@drawable/btn_radio_to_off_mtrl_008</item>
+ <item>@drawable/btn_radio_to_off_mtrl_009</item>
+ <item>@drawable/btn_radio_to_off_mtrl_010</item>
+ <item>@drawable/btn_radio_to_off_mtrl_011</item>
+ <item>@drawable/btn_radio_to_off_mtrl_012</item>
+ <item>@drawable/btn_radio_to_off_mtrl_013</item>
+ <item>@drawable/btn_radio_to_off_mtrl_014</item>
+ <item>@drawable/btn_radio_to_off_mtrl_015</item>
+ <item>@drawable/btn_radio_to_on_mtrl_000</item>
+ <item>@drawable/btn_radio_to_on_mtrl_001</item>
+ <item>@drawable/btn_radio_to_on_mtrl_002</item>
+ <item>@drawable/btn_radio_to_on_mtrl_003</item>
+ <item>@drawable/btn_radio_to_on_mtrl_004</item>
+ <item>@drawable/btn_radio_to_on_mtrl_005</item>
+ <item>@drawable/btn_radio_to_on_mtrl_006</item>
+ <item>@drawable/btn_radio_to_on_mtrl_007</item>
+ <item>@drawable/btn_radio_to_on_mtrl_008</item>
+ <item>@drawable/btn_radio_to_on_mtrl_009</item>
+ <item>@drawable/btn_radio_to_on_mtrl_010</item>
+ <item>@drawable/btn_radio_to_on_mtrl_011</item>
+ <item>@drawable/btn_radio_to_on_mtrl_012</item>
+ <item>@drawable/btn_radio_to_on_mtrl_013</item>
+ <item>@drawable/btn_radio_to_on_mtrl_014</item>
+ <item>@drawable/btn_radio_to_on_mtrl_015</item>
+ <item>@drawable/btn_rating_star_off_mtrl_alpha</item>
+ <item>@drawable/btn_rating_star_on_mtrl_alpha</item>
+ <item>@drawable/btn_star_mtrl_alpha</item>
+ <item>@drawable/btn_switch_to_off_mtrl_000</item>
+ <item>@drawable/btn_switch_to_off_mtrl_001</item>
+ <item>@drawable/btn_switch_to_off_mtrl_002</item>
+ <item>@drawable/btn_switch_to_off_mtrl_003</item>
+ <item>@drawable/btn_switch_to_off_mtrl_004</item>
+ <item>@drawable/btn_switch_to_off_mtrl_005</item>
+ <item>@drawable/btn_switch_to_off_mtrl_006</item>
+ <item>@drawable/btn_switch_to_off_mtrl_007</item>
+ <item>@drawable/btn_switch_to_off_mtrl_008</item>
+ <item>@drawable/btn_switch_to_off_mtrl_009</item>
+ <item>@drawable/btn_switch_to_off_mtrl_010</item>
+ <item>@drawable/btn_switch_to_off_mtrl_011</item>
+ <item>@drawable/btn_switch_to_off_mtrl_012</item>
+ <item>@drawable/btn_switch_to_off_mtrl_013</item>
+ <item>@drawable/btn_switch_to_off_mtrl_014</item>
+ <item>@drawable/btn_switch_to_on_mtrl_000</item>
+ <item>@drawable/btn_switch_to_on_mtrl_001</item>
+ <item>@drawable/btn_switch_to_on_mtrl_002</item>
+ <item>@drawable/btn_switch_to_on_mtrl_003</item>
+ <item>@drawable/btn_switch_to_on_mtrl_004</item>
+ <item>@drawable/btn_switch_to_on_mtrl_005</item>
+ <item>@drawable/btn_switch_to_on_mtrl_006</item>
+ <item>@drawable/btn_switch_to_on_mtrl_007</item>
+ <item>@drawable/btn_switch_to_on_mtrl_008</item>
+ <item>@drawable/btn_switch_to_on_mtrl_009</item>
+ <item>@drawable/btn_switch_to_on_mtrl_010</item>
+ <item>@drawable/btn_switch_to_on_mtrl_011</item>
+ <item>@drawable/btn_switch_to_on_mtrl_012</item>
+ <item>@drawable/btn_switch_to_on_mtrl_013</item>
+ <item>@drawable/btn_switch_to_on_mtrl_014</item>
+ <item>@drawable/btn_toggle_indicator_mtrl_alpha</item>
+ <item>@drawable/btn_toggle_mtrl_alpha</item>
+ <item>@drawable/expander_close_mtrl_alpha</item>
+ <item>@drawable/expander_open_mtrl_alpha</item>
+ <item>@drawable/fastscroll_thumb_mtrl_alpha</item>
+ <item>@drawable/fastscroll_track_mtrl_alpha</item>
+ <item>@drawable/ic_ab_back_mtrl_am_alpha</item>
+ <item>@drawable/ic_cab_done_mtrl_alpha</item>
+ <item>@drawable/ic_clear_mtrl_alpha</item>
+ <item>@drawable/ic_commit_search_api_mtrl_alpha</item>
+ <item>@drawable/ic_dialog_alert_mtrl_alpha</item>
+ <item>@drawable/ic_find_next_mtrl_alpha</item>
+ <item>@drawable/ic_find_previous_mtrl_alpha</item>
+ <item>@drawable/ic_go_search_api_mtrl_alpha</item>
+ <item>@drawable/ic_media_route_disabled_mtrl_alpha</item>
+ <item>@drawable/ic_media_route_off_mtrl_alpha</item>
+ <item>@drawable/ic_media_route_on_0_mtrl_alpha</item>
+ <item>@drawable/ic_media_route_on_1_mtrl_alpha</item>
+ <item>@drawable/ic_media_route_on_2_mtrl_alpha</item>
+ <item>@drawable/ic_media_route_on_mtrl_alpha</item>
+ <item>@drawable/ic_menu_copy_mtrl_am_alpha</item>
+ <item>@drawable/ic_menu_cut_mtrl_alpha</item>
+ <item>@drawable/ic_menu_find_mtrl_alpha</item>
+ <item>@drawable/ic_menu_moreoverflow_mtrl_alpha</item>
+ <item>@drawable/ic_menu_paste_mtrl_am_alpha</item>
+ <item>@drawable/ic_menu_search_mtrl_alpha</item>
+ <item>@drawable/ic_menu_selectall_mtrl_alpha</item>
+ <item>@drawable/ic_menu_share_mtrl_alpha</item>
+ <item>@drawable/ic_search_api_mtrl_alpha</item>
+ <item>@drawable/ic_voice_search_api_mtrl_alpha</item>
+ <item>@drawable/list_divider_mtrl_alpha</item>
+ <item>@drawable/list_section_divider_mtrl_alpha</item>
+ <item>@drawable/popup_background_mtrl_mult</item>
+ <item>@drawable/progress_primary_mtrl_alpha</item>
+ <item>@drawable/progress_mtrl_alpha</item>
+ <item>@drawable/scrollbar_handle_mtrl_alpha</item>
+ <item>@drawable/scrubber_control_from_pressed_mtrl_000</item>
+ <item>@drawable/scrubber_control_from_pressed_mtrl_001</item>
+ <item>@drawable/scrubber_control_from_pressed_mtrl_002</item>
+ <item>@drawable/scrubber_control_from_pressed_mtrl_003</item>
+ <item>@drawable/scrubber_control_from_pressed_mtrl_004</item>
+ <item>@drawable/scrubber_control_from_pressed_mtrl_005</item>
+ <item>@drawable/scrubber_control_off_pressed_mtrl_alpha</item>
+ <item>@drawable/scrubber_control_off_mtrl_alpha</item>
+ <item>@drawable/scrubber_control_on_pressed_mtrl_alpha</item>
+ <item>@drawable/scrubber_control_on_mtrl_alpha</item>
+ <item>@drawable/scrubber_control_to_pressed_mtrl_000</item>
+ <item>@drawable/scrubber_control_to_pressed_mtrl_001</item>
+ <item>@drawable/scrubber_control_to_pressed_mtrl_002</item>
+ <item>@drawable/scrubber_control_to_pressed_mtrl_003</item>
+ <item>@drawable/scrubber_control_to_pressed_mtrl_004</item>
+ <item>@drawable/scrubber_control_to_pressed_mtrl_005</item>
+ <item>@drawable/scrubber_primary_mtrl_alpha</item>
+ <item>@drawable/scrubber_track_mtrl_alpha</item>
+ <item>@drawable/spinner_mtrl_am_alpha</item>
+ <item>@drawable/switch_track_mtrl_alpha</item>
+ <item>@drawable/text_cursor_mtrl_alpha</item>
+ <item>@drawable/textfield_activated_mtrl_alpha</item>
+ <item>@drawable/textfield_default_mtrl_alpha</item>
+ <item>@drawable/textfield_search_activated_mtrl_alpha</item>
+ <item>@drawable/textfield_search_default_mtrl_alpha</item>
+ <item>@drawable/text_select_handle_left_mtrl_alpha</item>
+ <item>@drawable/text_select_handle_middle_mtrl_alpha</item>
+ <item>@drawable/text_select_handle_right_mtrl_alpha</item>
</array>
<!-- Do not translate. These are all of the color state list resources that should be
@@ -334,6 +517,18 @@
<item>#ff000000</item>
<item>#00000000</item>
<item>#ffffffff</item>
+
+ <!-- Material color state lists -->
+ <item>@color/background_cache_hint_selector_material_dark</item>
+ <item>@color/background_cache_hint_selector_material_light</item>
+ <item>@color/btn_default_material_dark</item>
+ <item>@color/btn_default_material_light</item>
+ <item>@color/primary_text_disable_only_material_dark</item>
+ <item>@color/primary_text_disable_only_material_light</item>
+ <item>@color/primary_text_material_dark</item>
+ <item>@color/primary_text_material_light</item>
+ <item>@color/search_url_text_material_dark</item>
+ <item>@color/search_url_text_material_light</item>
</array>
<!-- Used in LocalePicker -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 489adb4..5213896 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -496,6 +496,12 @@
<!-- Internal layout used internally for window decor -->
<attr name="windowActionBarFullscreenDecorLayout" format="reference" />
+ <!-- The duration, in milliseconds, of the window background fade duration
+ when transitioning into or away from an Activity when called with an
+ Activity Transition. Corresponds to
+ {@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. -->
+ <attr name="windowTransitionBackgroundFadeDuration" format="integer"/>
+
<!-- ============ -->
<!-- Alert Dialog styles -->
<!-- ============ -->
@@ -963,17 +969,15 @@
<eat-comment />
<!-- The primary branding color for the app. By default, this is the color applied to the
- action bar background and framework controls (via colorControlActivated). -->
+ action bar background. -->
<attr name="colorPrimary" format="color" />
<!-- Dark variant of the primary branding color. By default, this is the color applied to
the status bar (via statusBarColor) and navigation bar (via navigationBarColor). -->
<attr name="colorPrimaryDark" format="color" />
- <!-- Light variant of the primary branding color. TODO: Not used? -->
- <attr name="colorPrimaryLight" format="color" />
-
- <!-- Bright complement to the primary branding color. TODO: Not used? -->
+ <!-- Bright complement to the primary branding color. By default, this is the color applied
+ to framework controls (via colorControlActivated). -->
<attr name="colorAccent" format="color" />
<!-- The color applied to framework controls in their normal state. -->
@@ -982,7 +986,7 @@
<!-- The color applied to framework controls in their activated (ex. checked) state. -->
<attr name="colorControlActivated" format="color" />
- <!-- The color applied to framework control highlights (ex. ripples, selection). -->
+ <!-- The color applied to framework control highlights (ex. ripples, list selectors). -->
<attr name="colorControlHighlight" format="color" />
<!-- The color applied to framework buttons in their normal state. -->
@@ -1815,6 +1819,11 @@
Corresponds to {@link android.view.Window#setNavigationBarColor(int)}. -->
<attr name="navigationBarColor" format="color" />
+ <!-- The duration, in milliseconds, of the window background fade duration
+ when transitioning into or away from an Activity when called with an
+ Activity Transition. Corresponds to
+ {@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. -->
+ <attr name="windowTransitionBackgroundFadeDuration" />
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
@@ -3861,8 +3870,11 @@
<attr name="inputType" />
</declare-styleable>
<declare-styleable name="PopupWindow">
+ <!-- The background to use for the popup window. -->
<attr name="popupBackground" format="reference|color" />
+ <!-- The animation style to use for the popup window. -->
<attr name="popupAnimationStyle" format="reference" />
+ <!-- Whether the popup window should overlap its anchor view. -->
<attr name="overlapAnchor" format="boolean" />
</declare-styleable>
<declare-styleable name="ViewAnimator">
@@ -4564,6 +4576,16 @@
<attr name="drawable" />
</declare-styleable>
+ <!-- Drawable used to render the Material progress indicator. -->
+ <declare-styleable name="MaterialProgressDrawable">
+ <attr name="visible" />
+ <attr name="thickness" />
+ <attr name="innerRadius" />
+ <attr name="width" />
+ <attr name="height" />
+ <attr name="color" />
+ </declare-styleable>
+
<declare-styleable name="InsetDrawable">
<attr name="visible" />
<attr name="drawable" />
@@ -4667,12 +4689,8 @@
<!-- Drawable used to show animated touch feedback. -->
<declare-styleable name="RippleDrawable">
- <!-- The tint to use for ripple effects. This attribute is required. -->
- <attr name="tint" />
- <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default value is src_atop, which draws over the opaque parts of the drawable. -->
- <attr name="tintMode" />
- <!-- Whether to pin ripple effects to the center of the drawable. Default value is false. -->
- <attr name="pinned" format="boolean" />
+ <!-- The color to use for ripple effects. This attribute is required. -->
+ <attr name="color" />
</declare-styleable>
<declare-styleable name="ScaleDrawable">
@@ -4768,7 +4786,7 @@
</declare-styleable>
<!-- ========================== -->
- <!-- Vector drawable class -->
+ <!-- VectorDrawable class -->
<!-- ========================== -->
<eat-comment />
@@ -4788,7 +4806,7 @@
<attr name="height" />
</declare-styleable>
- <!-- Defines the group used in Vector Drawables. -->
+ <!-- Defines the group used in VectorDrawables. -->
<declare-styleable name="VectorDrawableGroup">
<!-- The Name of this group -->
<attr name="name" />
@@ -4806,9 +4824,11 @@
<attr name="scaleX" />
<!-- The amount to scale the group on X coordinate -->
<attr name="scaleY" />
+ <!-- The alpha of the group (0 is transparent and 1 is opaque) -->
+ <attr name="alpha" />
</declare-styleable>
- <!-- Defines the path used in Vector Drawables. -->
+ <!-- Defines the path used in VectorDrawables. -->
<declare-styleable name="VectorDrawablePath">
<!-- The Name of this path -->
<attr name="name" />
@@ -4849,6 +4869,25 @@
</declare-styleable>
<!-- ========================== -->
+ <!-- AnimatedVectorDrawable class -->
+ <!-- ========================== -->
+ <eat-comment />
+
+ <!-- Define the AnimatedVectorDrawable. -->
+ <declare-styleable name="AnimatedVectorDrawable">
+ <!-- The static vector drawable. -->
+ <attr name="drawable" />
+ </declare-styleable>
+
+ <!-- Defines the target path or group used in the AnimatedVectorDrawable. -->
+ <declare-styleable name="AnimatedVectorDrawableTarget">
+ <!-- The name of this target path or group -->
+ <attr name="name" />
+ <!-- The animation for this target path or group -->
+ <attr name="animation" />
+ </declare-styleable>
+
+ <!-- ========================== -->
<!-- Animation class attributes -->
<!-- ========================== -->
<eat-comment />
@@ -6359,6 +6398,12 @@
<attr name="aspect" format="string" />
<!-- Color to use when drawing LockPatternView paths. -->
<attr name="pathColor" format="color|reference" />
+ <!-- The regular pattern color -->
+ <attr name="regularColor" format="color|reference" />
+ <!-- The error color -->
+ <attr name="errorColor" format="color|reference" />
+ <!-- The success color -->
+ <attr name="successColor" format="color|reference"/>
</declare-styleable>
<!-- Use <code>recognition-service</code> as the root tag of the XML resource that
@@ -6378,6 +6423,16 @@
<attr name="settingsActivity" />
</declare-styleable>
+ <!-- Use <code>voice-enrollment-application</code>
+ as the root tag of the XML resource that escribes the supported keyphrases (hotwords)
+ by the enrollment application.
+ Described here are the attributes that can be included in that tag. -->
+ <declare-styleable name="VoiceEnrollmentApplication">
+ <attr name="searchKeyphraseId" format="integer" />
+ <attr name="searchKeyphrase" format="string" />
+ <attr name="searchKeyphraseSupportedLocales" format="string" />
+ </declare-styleable>
+
<!-- Attributes used to style the Action Bar. -->
<declare-styleable name="ActionBar">
<!-- The type of navigation to use. -->
@@ -6717,6 +6772,33 @@
<!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. -->
<declare-styleable name="EdgeEffect">
- <attr name="colorPrimaryLight" />
+ <attr name="colorPrimary" />
+ </declare-styleable>
+
+ <!-- Use <code>tv-input</code> as the root tag of the XML resource that describes an
+ {@link android.media.tv.TvInputService}, which is referenced from its
+ {@link android.media.tv.TvInputService#SERVICE_META_DATA} meta-data entry.
+ Described here are the attributes that can be included in that tag. -->
+ <declare-styleable name="TvInputService">
+ <!-- Component name of an activity for setup of this service.
+ The setup includes scanning channels and registering EPG data. -->
+ <attr name="setupActivity" format="string" />
+ <!-- Component name of an activity that allows the user to modify
+ the settings for this service. -->
+ <attr name="settingsActivity" />
+ <!-- Type of this service. -->
+ <attr name="tvInputType">
+ <!-- Should be in sync with constant values defined in
+ {@link android.media.tv.TvInputInfo}. -->
+
+ <!-- Virtual input (default) -->
+ <enum name="virtual" value="0" />
+ <!-- HDMI -->
+ <enum name="hdmi" value="1" />
+ <!-- Built-in tuner -->
+ <enum name="tuner" value="2" />
+ <!-- Pass-through -->
+ <enum name="passthrough" value="3" />
+ </attr>
</declare-styleable>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index acfbe2d..814d8fc 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -898,16 +898,22 @@
android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} to every Intent used to launch
the activity.
- <p>The documentLaunchMode attribute may be assigned one of three values, "none",
- "intoExisting" and "always", described in detail below. 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>.
+ <p>The documentLaunchMode attribute may be assigned one of four values, "none",
+ "intoExisting", "always" and "never", described in detail below. For values other than
+ <code>none</code> and <code>never</code> the activity must be defined with
+ {@link android.R.attr#launchMode} <code>standard</code>.
If this attribute is not specified, <code>none</code> will be used.
Note that <code>none</code> can be overridden at run time if the Intent used
- to launch it contains the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+ to launch it contains the flag {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+ Intent.FLAG_ACTIVITY_NEW_DOCUMENT}.
Similarly <code>intoExisting</code> will be overridden by the flag
- {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} combined with
- {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+ Intent.FLAG_ACTIVITY_NEW_DOCUMENT} combined with
+ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. If the value of
+ documentLaunchModes is <code>never</code> then any use of
+.........{@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+ Intent.FLAG_ACTIVITY_NEW_DOCUMENT} to launch this activity will be ignored. -->
<attr name="documentLaunchMode">
<!-- The default mode, which will create a new task only when
{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
@@ -931,8 +937,21 @@
and {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
Intent.FLAG_ACTIVITY_MULTIPLE_TASK} both set. -->
<enum name="always" value="2" />
+ <!-- This activity will not be launched into a new document even if the Intent contains
+ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+ Intent.FLAG_ACTIVITY_NEW_DOCUMENT}. This gives the activity writer ultimate
+ control over how their activity is used. Note that applications prior to api
+ 21 will default to documentLaunchMode="none" so only activities that explicitly
+ opt out with <code>"never"</code> may do so. -->
+ <enum name="never" value="3" />
</attr>
+ <!-- The maximum number of entries of tasks rooted at this activity in the recent task list.
+ When this number of entries is reached the least recently used instance of this activity
+ will be removed from recents. The value will be clamped between 1 and 100 inclusive.
+ The default value for this if it is not specified is 15. -->
+ <attr name="maxRecents" format="integer" />
+
<!-- Tasks launched by activities with this attribute will remain in the recent tasks
list until the last activity in the task is completed. When that happens the task
will be automatically removed from the recent tasks list.
@@ -1607,6 +1626,7 @@
<attr name="persistable" />
<attr name="allowEmbedded" />
<attr name="documentLaunchMode" />
+ <attr name="maxRecents" />
<attr name="autoRemoveFromRecents" />
</declare-styleable>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 5a2609e..9f6c7ad 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -115,6 +115,11 @@
<color name="kg_multi_user_text_inactive">#ff808080</color>
<color name="kg_widget_pager_gradient">#ffffffff</color>
+ <!-- LockPatternView -->
+ <color name="lock_pattern_view_regular_color">#ffffffff</color>
+ <color name="lock_pattern_view_success_color">#ffffffff</color>
+ <color name="lock_pattern_view_error_color">#fff4511e</color>
+
<!-- FaceLock -->
<color name="facelock_spotlight_mask">#CC000000</color>
@@ -130,7 +135,6 @@
<color name="notification_action_legacy_color_filter">#ff555555</color>
<color name="notification_media_action_bg">#00000000</color>
- <color name="notification_media_info_bg">#40FFFFFF</color>
<color name="notification_media_progress">#FFFFFFFF</color>
<!-- Keyguard colors -->
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index d1f4e38..97b4803 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -75,7 +75,7 @@
<!-- A really bright Holo shade of gray -->
<color name="holo_gray_bright">#33CCCCCC</color>
- <!-- Forward compatibility for Quantum-style theme colors -->
+ <!-- Forward compatibility for Material-style theme colors -->
<eat-comment />
<color name="holo_primary_dark">#ff000000</color>
diff --git a/core/res/res/values/colors_legacy.xml b/core/res/res/values/colors_legacy.xml
index 48d4b42..ad22845 100644
--- a/core/res/res/values/colors_legacy.xml
+++ b/core/res/res/values/colors_legacy.xml
@@ -24,7 +24,7 @@
<color name="legacy_selected_highlight">#fff17a0a</color>
<color name="legacy_long_pressed_highlight">#ffffffff</color>
- <!-- Forward compatibility for Quantum-style theme colors -->
+ <!-- Forward compatibility for Material-style theme colors -->
<eat-comment />
<color name="legacy_primary_dark">#ff000000</color>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
new file mode 100644
index 0000000..faa9fb7
--- /dev/null
+++ b/core/res/res/values/colors_material.xml
@@ -0,0 +1,169 @@
+<?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.
+-->
+
+<!-- Colors specific to Material themes. -->
+<resources>
+ <color name="background_material_dark">#ff212121</color>
+ <color name="background_material_light">#fffafafa</color>
+
+ <color name="ripple_material_light">#20444444</color>
+ <color name="ripple_material_dark">#20ffffff</color>
+
+ <color name="button_material_dark">#ff5a595b</color>
+ <color name="button_material_light">#ffd6d7d7</color>
+
+ <color name="bright_foreground_material_dark">@color/white</color>
+ <color name="bright_foreground_material_light">@color/black</color>
+ <!-- Black 50% -->
+ <color name="bright_foreground_disabled_material_dark">#80000000</color>
+ <!-- White 50% -->
+ <color name="bright_foreground_disabled_material_light">#80ffffff</color>
+ <color name="bright_foreground_inverse_material_dark">@color/bright_foreground_material_light</color>
+ <color name="bright_foreground_inverse_material_light">@color/bright_foreground_material_dark</color>
+
+ <color name="dim_foreground_material_dark">#ffbebebe</color>
+ <color name="dim_foreground_material_light">#ff323232</color>
+ <color name="dim_foreground_disabled_material_dark">#80bebebe</color>
+ <color name="dim_foreground_disabled_material_light">#80323232</color>
+
+ <color name="hint_foreground_material_dark">@color/bright_foreground_disabled_material_dark</color>
+ <color name="hint_foreground_material_light">@color/bright_foreground_disabled_material_light</color>
+ <!-- TODO: This is 40% alpha on the default accent color. -->
+ <color name="highlighted_text_material_dark">#6640c4ff</color>
+ <!-- TODO: This is 40% alpha on the default accent color. -->
+ <color name="highlighted_text_material_light">#6640c4ff</color>
+
+ <!-- Text & foreground colors -->
+ <eat-comment />
+
+ <!-- Black 87% -->
+ <color name="primary_text_default_material_light">#de000000</color>
+ <!-- Black 54% -->
+ <color name="secondary_text_material_light">#8a000000</color>
+ <!-- Black 54% (TODO: same as secondary?) -->
+ <color name="tertiary_text_material_light">#8a000000</color>
+
+ <!-- White 87% -->
+ <color name="primary_text_default_material_dark">#deffffff</color>
+ <!-- White 38% -->
+ <color name="secondary_text_material_dark">#61ffffff</color>
+ <!-- White 38% (TODO: same as secondary?) -->
+ <color name="tertiary_text_material_dark">#61ffffff</color>
+
+ <!-- Primary & accent colors -->
+ <eat-comment />
+
+ <color name="material_red_100">#fff4c7c3</color>
+ <color name="material_red_300">#ffe67c73</color>
+ <color name="material_red_500">#ffdb4437</color>
+ <color name="material_red_700">#ffc53929</color>
+ <color name="material_red_A200">#ffff5252</color>
+ <color name="material_red_A400">#ffff1744</color>
+
+ <color name="material_blue_100">#ffc6dafc</color>
+ <color name="material_blue_300">#ff7baaf7</color>
+ <color name="material_blue_500">#ff4285f4</color>
+ <color name="material_blue_700">#ff3367d6</color>
+ <color name="material_blue_A200">#ff448aff</color>
+ <color name="material_blue_A400">#ff2979ff</color>
+
+ <color name="material_light_blue_A200">#ff40c4ff</color>
+
+ <color name="material_teal_100">#ffb2ebf2</color>
+ <color name="material_teal_300">#ff4dd0e1</color>
+ <color name="material_teal_500">#ff00bcd4</color>
+ <color name="material_teal_700">#ff0097a7</color>
+ <color name="material_teal_A200">#ff18ffff</color>
+ <color name="material_teal_A400">#ff00e5ff</color>
+
+ <!-- Accent color used by Settings -->
+ <color name="material_dark_teal_A400">#ff009688</color>
+
+ <color name="material_green_100">#ffb7e1cd</color>
+ <color name="material_green_300">#ff57bb8a</color>
+ <color name="material_green_500">#ff0f9d58</color>
+ <color name="material_green_700">#ff0b8043</color>
+ <color name="material_green_A200">#ff69f0ae</color>
+ <color name="material_green_A400">#ff00e676</color>
+
+ <color name="material_lime_100">#fff0f4c3</color>
+ <color name="material_lime_300">#ffdce775</color>
+ <color name="material_lime_500">#ffcddc39</color>
+ <color name="material_lime_700">#ffafb42b</color>
+ <color name="material_lime_A200">#ffeeff41</color>
+ <color name="material_lime_A400">#ffc6ff00</color>
+
+ <color name="material_yellow_100">#fffce8b2</color>
+ <color name="material_yellow_300">#fff7cb4d</color>
+ <color name="material_yellow_500">#fff4b400</color>
+ <color name="material_yellow_700">#fff09300</color>
+ <color name="material_yellow_A200">#ffffcd40</color>
+ <color name="material_yellow_A400">#ffffbc00</color>
+
+ <color name="material_orange_100">#ffffe0b2</color>
+ <color name="material_orange_300">#ffffb74d</color>
+ <color name="material_orange_500">#ffff9800</color>
+ <color name="material_orange_700">#fff57c00</color>
+ <color name="material_orange_A200">#ffffab40</color>
+ <color name="material_orange_A400">#ffff9100</color>
+
+ <color name="material_deep_orange_100">#fff4c7c3</color>
+ <color name="material_deep_orange_300">#ffe67c73</color>
+ <color name="material_deep_orange_500">#ffff5722</color>
+ <color name="material_deep_orange_700">#ffc53929</color>
+ <color name="material_deep_orange_A200">#ffff5252</color>
+ <color name="material_deep_orange_A400">#ffff1744</color>
+
+ <!-- Neutral colors -->
+ <eat-comment />
+
+ <color name="material_grey_50">#fffafafa</color>
+ <color name="material_grey_100">#fff5f5f5</color>
+ <color name="material_grey_300">#ffeeeeee</color>
+ <color name="material_grey_500">#ffa3a3a3</color>
+ <color name="material_grey_600">#ff757575</color>
+ <color name="material_grey_700">#ff717171</color>
+ <color name="material_grey_900">#ff212121</color>
+
+ <color name="material_blue_grey_50">#ffeceff1</color>
+ <color name="material_blue_grey_100">#ffcfd8dc</color>
+ <color name="material_blue_grey_300">#ff90a4ae</color>
+ <color name="material_blue_grey_400">#ff78909c</color>
+ <color name="material_blue_grey_500">#ff607d8b</color>
+ <color name="material_blue_grey_600">#ff546e7a</color>
+ <color name="material_blue_grey_700">#ff455a64</color>
+ <color name="material_blue_grey_800">#ff37474f</color>
+ <!-- Primary color used by Settings -->
+ <color name="material_blue_grey_900">#ff263238</color>
+ <!-- Primary dark color used by Settings -->
+ <color name="material_blue_grey_950">#ff21272b</color>
+
+ <color name="material_brown_100">#ffd7ccc8</color>
+ <color name="material_brown_300">#ffa1887f</color>
+ <color name="material_brown_500">#ff795548</color>
+ <color name="material_brown_700">#ff5d4037</color>
+
+ <!-- Time picker defaults when no theme is set -->
+ <eat-comment />
+
+ <color name="timepicker_default_background_material">@color/primary_text_default_material_light</color>
+ <color name="timepicker_default_text_color_material">@color/black</color>
+ <color name="timepicker_default_disabled_color_material">@color/bright_foreground_disabled_material_dark</color>
+ <color name="timepicker_default_ampm_selected_background_color_material">@color/material_light_blue_A200</color>
+ <color name="timepicker_default_ampm_unselected_background_color_material">@color/transparent</color>
+ <color name="timepicker_default_selector_color_material">@color/material_light_blue_A200</color>
+ <color name="timepicker_default_numbers_background_color_material">@color/transparent</color>
+</resources>
diff --git a/core/res/res/values/colors_quantum.xml b/core/res/res/values/colors_quantum.xml
deleted file mode 100644
index 976930c..0000000
--- a/core/res/res/values/colors_quantum.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-<?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.
--->
-
-<!-- Colors specific to Quantum themes. -->
-<resources>
- <color name="background_quantum_dark">#ff414042</color>
- <color name="background_quantum_light">#fff1f2f2</color>
-
- <color name="ripple_quantum_dark">#30ffffff</color>
- <color name="ripple_quantum_light">#30000000</color>
-
- <color name="button_quantum_dark">#ff5a595b</color>
- <color name="button_quantum_light">#ffd6d7d7</color>
-
- <color name="bright_foreground_quantum_dark">@color/white</color>
- <color name="bright_foreground_quantum_light">@color/black</color>
- <!-- TODO: This is 50% alpha black -->
- <color name="bright_foreground_disabled_quantum_dark">#80000000</color>
- <!-- TODO: This is 50% alpha white -->
- <color name="bright_foreground_disabled_quantum_light">#80ffffff</color>
- <color name="bright_foreground_inverse_quantum_dark">@color/bright_foreground_quantum_light</color>
- <color name="bright_foreground_inverse_quantum_light">@color/bright_foreground_quantum_dark</color>
-
- <color name="dim_foreground_quantum_dark">#ffbebebe</color>
- <color name="dim_foreground_quantum_light">#ff323232</color>
- <color name="dim_foreground_disabled_quantum_dark">#80bebebe</color>
- <color name="dim_foreground_disabled_quantum_light">#80323232</color>
-
- <color name="hint_foreground_quantum_dark">@color/bright_foreground_disabled_quantum_dark</color>
- <color name="hint_foreground_quantum_light">@color/bright_foreground_disabled_quantum_light</color>
- <!-- TODO: This is 40% alpha teal_A200 -->
- <color name="highlighted_text_quantum_dark">#660097a7</color>
- <!-- TODO: This is 40% alpha teal_A200 -->
- <color name="highlighted_text_quantum_light">#660097a7</color>
-
- <!-- Primary & accent colors -->
- <eat-comment />
-
- <color name="quantum_red_100">#fff4c7c3</color>
- <color name="quantum_red_300">#ffe67c73</color>
- <color name="quantum_red_500">#ffdb4437</color>
- <color name="quantum_red_700">#ffc53929</color>
- <color name="quantum_red_A200">#ffff5252</color>
- <color name="quantum_red_A400">#ffff1744</color>
-
- <color name="quantum_blue_100">#ffc6dafc</color>
- <color name="quantum_blue_300">#ff7baaf7</color>
- <color name="quantum_blue_500">#ff4285f4</color>
- <color name="quantum_blue_700">#ff3367d6</color>
- <color name="quantum_blue_A200">#ff448aff</color>
- <color name="quantum_blue_A400">#ff2979ff</color>
-
- <color name="quantum_teal_100">#ffb2ebf2</color>
- <color name="quantum_teal_300">#ff4dd0e1</color>
- <color name="quantum_teal_500">#ff00bcd4</color>
- <color name="quantum_teal_700">#ff0097a7</color>
- <color name="quantum_teal_A200">#ff18ffff</color>
- <color name="quantum_teal_A400">#ff00e5ff</color>
-
- <color name="quantum_green_100">#ffb7e1cd</color>
- <color name="quantum_green_300">#ff57bb8a</color>
- <color name="quantum_green_500">#ff0f9d58</color>
- <color name="quantum_green_700">#ff0b8043</color>
- <color name="quantum_green_A200">#ff69f0ae</color>
- <color name="quantum_green_A400">#ff00e676</color>
-
- <color name="quantum_lime_100">#fff0f4c3</color>
- <color name="quantum_lime_300">#ffdce775</color>
- <color name="quantum_lime_500">#ffcddc39</color>
- <color name="quantum_lime_700">#ffafb42b</color>
- <color name="quantum_lime_A200">#ffeeff41</color>
- <color name="quantum_lime_A400">#ffc6ff00</color>
-
- <color name="quantum_yellow_100">#fffce8b2</color>
- <color name="quantum_yellow_300">#fff7cb4d</color>
- <color name="quantum_yellow_500">#fff4b400</color>
- <color name="quantum_yellow_700">#fff09300</color>
- <color name="quantum_yellow_A200">#ffffcd40</color>
- <color name="quantum_yellow_A400">#ffffbc00</color>
-
- <color name="quantum_orange_100">#ffffe0b2</color>
- <color name="quantum_orange_300">#ffffb74d</color>
- <color name="quantum_orange_500">#ffff9800</color>
- <color name="quantum_orange_700">#fff57c00</color>
- <color name="quantum_orange_A200">#ffffab40</color>
- <color name="quantum_orange_A400">#ffff9100</color>
-
- <color name="quantum_deep_orange_100">#fff4c7c3</color>
- <color name="quantum_deep_orange_300">#ffe67c73</color>
- <color name="quantum_deep_orange_500">#ffff5722</color>
- <color name="quantum_deep_orange_700">#ffc53929</color>
- <color name="quantum_deep_orange_A200">#ffff5252</color>
- <color name="quantum_deep_orange_A400">#ffff1744</color>
-
- <!-- Neutral colors -->
- <eat-comment />
-
- <color name="quantum_grey_50">#fffafafa</color>
- <color name="quantum_grey_100">#fff5f5f5</color>
- <color name="quantum_grey_300">#ffeeeeee</color>
- <color name="quantum_grey_500">#ffa3a3a3</color>
- <color name="quantum_grey_700">#ff717171</color>
-
- <color name="quantum_blue_grey_50">#ffeceff1</color>
- <color name="quantum_blue_grey_100">#ffcfd8dc</color>
- <color name="quantum_blue_grey_300">#ff90a4ae</color>
- <color name="quantum_blue_grey_500">#ff607d8b</color>
- <color name="quantum_blue_grey_700">#ff455a64</color>
-
- <color name="quantum_brown_100">#ffd7ccc8</color>
- <color name="quantum_brown_300">#ffa1887f</color>
- <color name="quantum_brown_500">#ff795548</color>
- <color name="quantum_brown_700">#ff5d4037</color>
-
- <!-- Text & foreground colors -->
- <eat-comment />
-
- <color name="primary_text_default_quantum_light">#de000000</color>
- <color name="secondary_text_quantum_light">#8a000000</color>
- <color name="tertiary_text_quantum_light">#4d000000</color>
-
- <color name="primary_text_default_quantum_dark">#deffffff</color>
- <color name="secondary_text_quantum_dark">#8affffff</color>
- <color name="tertiary_text_quantum_dark">#4dffffff</color>
-</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f6732d3..830c64c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -364,6 +364,9 @@
<!-- If this is true, the screen will come on when you unplug usb/power/whatever. -->
<bool name="config_unplugTurnsOnScreen">false</bool>
+ <!-- Set this true only if the device has separate attention and notification lights. -->
+ <bool name="config_useAttentionLight">false</bool>
+
<!-- If this is true, the screen will fade off. -->
<bool name="config_animateScreenLights">true</bool>
@@ -586,7 +589,7 @@
<!-- Display low battery warning when battery level dips to this value.
Also, the battery stats are flushed to disk when we hit this level. -->
- <integer name="config_criticalBatteryWarningLevel">4</integer>
+ <integer name="config_criticalBatteryWarningLevel">5</integer>
<!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
<integer name="config_shutdownBatteryTemperature">680</integer>
@@ -594,8 +597,9 @@
<!-- Display low battery warning when battery level dips to this value -->
<integer name="config_lowBatteryWarningLevel">15</integer>
- <!-- Close low battery warning when battery level reaches this value -->
- <integer name="config_lowBatteryCloseWarningLevel">20</integer>
+ <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
+ plus this -->
+ <integer name="config_lowBatteryCloseWarningBump">5</integer>
<!-- Default color for notification LED. -->
<color name="config_defaultNotificationColor">#ffffffff</color>
@@ -624,6 +628,9 @@
<!-- Default value for LED off time when the battery is low on charge in miliseconds -->
<integer name="config_notificationsBatteryLedOff">2875</integer>
+ <!-- Number of notifications to keep in the notification service historical archive -->
+ <integer name="config_notificationServiceArchiveSize">250</integer>
+
<!-- Allow the menu hard key to be disabled in LockScreen on some devices -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
@@ -784,6 +791,19 @@
config_enableFusedLocationOverlay is false. -->
<string name="config_fusedLocationProviderPackageName" translatable="false">com.android.location.fused</string>
+ <!-- Whether to enable Hardware FLP overlay which allows Hardware FLP to be
+ replaced by an app at run-time. When disabled, only the
+ config_hardwareFlpPackageName package will be searched for Hardware Flp,
+ otherwise packages whose signature matches the signatures of
+ config_locationProviderPackageNames will be searched, and the service
+ with the highest version number will be picked. Anyone who wants to
+ disable the overlay mechanism can set it to false.
+ -->
+ <bool name="config_enableHardwareFlpOverlay" translatable="false">true</bool>
+ <!-- Package name providing Hardware Flp. Used only when
+ config_enableHardwareFlpOverlay is false. -->
+ <string name="config_hardwareFlpPackageName" translatable="false">com.android.location.fused</string>
+
<!-- Whether to enable geocoder overlay which allows geocoder to be replaced
by an app at run-time. When disabled, only the
config_geocoderProviderPackageName package will be searched for
@@ -858,6 +878,14 @@
<!-- Boolean indicating if current platform supports BLE peripheral mode -->
<bool name="config_bluetooth_le_peripheral_mode_supported">false</bool>
+ <!-- Max number of scan filters supported by blutooth controller. 0 if the
+ device does not support hardware scan filters-->
+ <integer translatable="false" name="config_bluetooth_max_scan_filters">0</integer>
+
+ <!-- Max number of advertisers supported by bluetooth controller. 0 if the
+ device does not support multiple advertisement-->
+ <integer translatable="false" name="config_bluetooth_max_advertisers">0</integer>
+
<!-- The default data-use polling period. -->
<integer name="config_datause_polling_period_sec">600</integer>
@@ -1434,6 +1462,10 @@
<string name="config_customAdbPublicKeyConfirmationComponent"
>com.android.systemui/com.android.systemui.usb.UsbDebuggingActivity</string>
+ <!-- Name of the CustomDialog that is used for VPN -->
+ <string name="config_customVpnConfirmDialogComponent"
+ >com.android.vpndialogs/com.android.vpndialogs.CustomDialog</string>
+
<!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
<string name="config_appsAuthorizedForSharedAccounts">;com.android.settings;</string>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 657f614..e94b9dd 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -365,7 +365,7 @@
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
<dimen name="immersive_mode_cling_width">-1px</dimen>
- <!-- radius of the corners of the quantum rounded rect background -->
- <dimen name="notification_quantum_rounded_rect_radius">2dp</dimen>
+ <!-- radius of the corners of the material rounded rect background -->
+ <dimen name="notification_material_rounded_rect_radius">2dp</dimen>
</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
new file mode 100644
index 0000000..be7e6c1
--- /dev/null
+++ b/core/res/res/values/dimens_material.xml
@@ -0,0 +1,58 @@
+<?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>
+
+ <!-- Default height of an action bar. -->
+ <dimen name="action_bar_default_height_material">56dp</dimen>
+ <!-- Default padding of an action bar. -->
+ <dimen name="action_bar_default_padding_material">4dp</dimen>
+ <!-- Vertical padding around action bar icons. -->
+ <dimen name="action_bar_icon_vertical_padding_material">16dp</dimen>
+ <!-- Top margin for action bar subtitles -->
+ <dimen name="action_bar_subtitle_top_margin_material">-3dp</dimen>
+ <!-- Bottom margin for action bar subtitles -->
+ <dimen name="action_bar_subtitle_bottom_margin_material">5dp</dimen>
+
+ <dimen name="action_button_min_width_material">48dp</dimen>
+ <dimen name="action_button_min_height_material">48dp</dimen>
+ <dimen name="action_overflow_min_width_material">36dp</dimen>
+
+ <dimen name="text_size_display_4_material">112sp</dimen>
+ <dimen name="text_size_display_3_material">56sp</dimen>
+ <dimen name="text_size_display_2_material">45sp</dimen>
+ <dimen name="text_size_display_1_material">34sp</dimen>
+ <dimen name="text_size_headline_material">24sp</dimen>
+ <dimen name="text_size_title_material">20sp</dimen>
+ <dimen name="text_size_subhead_material">16sp</dimen>
+ <dimen name="text_size_body_2_material">14sp</dimen>
+ <dimen name="text_size_body_1_material">14sp</dimen>
+ <dimen name="text_size_caption_material">12sp</dimen>
+ <dimen name="text_size_menu_material">14sp</dimen>
+ <dimen name="text_size_button_material">14sp</dimen>
+
+ <dimen name="text_size_large_material">22sp</dimen>
+ <dimen name="text_size_medium_material">18sp</dimen>
+ <dimen name="text_size_small_material">14sp</dimen>
+
+ <dimen name="floating_window_z">16dp</dimen>
+ <dimen name="floating_window_margin_left">16dp</dimen>
+ <dimen name="floating_window_margin_top">8dp</dimen>
+ <dimen name="floating_window_margin_right">16dp</dimen>
+ <dimen name="floating_window_margin_bottom">32dp</dimen>
+
+ <!-- Amount of elevation for pressed button state -->
+ <dimen name="button_pressed_z">2dp</dimen>
+</resources>
diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml
deleted file mode 100644
index b5ba1ca..0000000
--- a/core/res/res/values/dimens_quantum.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?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>
-
- <!-- Default height of an action bar. -->
- <dimen name="action_bar_default_height_quantum">56dp</dimen>
- <!-- Default padding of an action bar. -->
- <dimen name="action_bar_default_padding_quantum">4dp</dimen>
- <!-- Vertical padding around action bar icons. -->
- <dimen name="action_bar_icon_vertical_padding_quantum">16dp</dimen>
- <!-- Text size for action bar titles -->
- <dimen name="action_bar_title_text_size_quantum">20sp</dimen>
- <!-- Text size for action bar subtitles -->
- <dimen name="action_bar_subtitle_text_size_quantum">16sp</dimen>
- <!-- Top margin for action bar subtitles -->
- <dimen name="action_bar_subtitle_top_margin_quantum">-3dp</dimen>
- <!-- Bottom margin for action bar subtitles -->
- <dimen name="action_bar_subtitle_bottom_margin_quantum">5dp</dimen>
-
- <dimen name="action_button_min_width_quantum">48dp</dimen>
- <dimen name="action_button_min_height_quantum">48dp</dimen>
- <dimen name="action_overflow_min_width_quantum">36dp</dimen>
-
- <dimen name="text_size_display_4_quantum">112sp</dimen>
- <dimen name="text_size_display_3_quantum">56sp</dimen>
- <dimen name="text_size_display_2_quantum">45sp</dimen>
- <dimen name="text_size_display_1_quantum">34sp</dimen>
- <dimen name="text_size_headline_quantum">24sp</dimen>
- <dimen name="text_size_title_quantum">20sp</dimen>
- <dimen name="text_size_subhead_quantum">16sp</dimen>
- <dimen name="text_size_body_2_quantum">14sp</dimen>
- <dimen name="text_size_body_1_quantum">14sp</dimen>
- <dimen name="text_size_caption_quantum">12sp</dimen>
- <dimen name="text_size_menu_quantum">14sp</dimen>
- <dimen name="text_size_button_quantum">14sp</dimen>
-
- <dimen name="text_size_large_quantum">22sp</dimen>
- <dimen name="text_size_medium_quantum">18sp</dimen>
- <dimen name="text_size_small_quantum">14sp</dimen>
-
- <dimen name="floating_window_z">16dp</dimen>
- <dimen name="floating_window_margin_left">16dp</dimen>
- <dimen name="floating_window_margin_top">8dp</dimen>
- <dimen name="floating_window_margin_right">16dp</dimen>
- <dimen name="floating_window_margin_bottom">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/donottranslate_material.xml b/core/res/res/values/donottranslate_material.xml
new file mode 100644
index 0000000..9ed9553
--- /dev/null
+++ b/core/res/res/values/donottranslate_material.xml
@@ -0,0 +1,32 @@
+<?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>
+
+ <string name="font_family_display_4_material">sans-serif-light</string>
+ <string name="font_family_display_3_material">sans-serif</string>
+ <string name="font_family_display_2_material">sans-serif</string>
+ <string name="font_family_display_1_material">sans-serif</string>
+ <string name="font_family_headline_material">sans-serif</string>
+ <string name="font_family_title_material">sans-serif-medium</string>
+ <string name="font_family_subhead_material">sans-serif</string>
+ <string name="font_family_body_2_material">sans-serif-medium</string>
+ <string name="font_family_body_1_material">sans-serif</string>
+ <string name="font_family_caption_material">sans-serif</string>
+ <string name="font_family_menu_material">sans-serif-medium</string>
+ <string name="font_family_button_material">sans-serif-medium</string>
+
+</resources>
diff --git a/core/res/res/values/donottranslate_quantum.xml b/core/res/res/values/donottranslate_quantum.xml
deleted file mode 100644
index e53c40e..0000000
--- a/core/res/res/values/donottranslate_quantum.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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>
-
- <string name="font_family_display_4_quantum">sans-serif-light</string>
- <string name="font_family_display_3_quantum">sans-serif</string>
- <string name="font_family_display_2_quantum">sans-serif</string>
- <string name="font_family_display_1_quantum">sans-serif</string>
- <string name="font_family_headline_quantum">sans-serif</string>
- <string name="font_family_title_quantum">sans-serif-medium</string>
- <string name="font_family_subhead_quantum">sans-serif</string>
- <string name="font_family_body_2_quantum">sans-serif-medium</string>
- <string name="font_family_body_1_quantum">sans-serif</string>
- <string name="font_family_caption_quantum">sans-serif</string>
- <string name="font_family_menu_quantum">sans-serif-medium</string>
- <string name="font_family_button_quantum">sans-serif-medium</string>
-
-</resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index c966a12..639091e 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -23,6 +23,7 @@
<item type="id" name="empty" />
<item type="id" name="hint" />
<item type="id" name="icon" />
+ <item type="id" name="icon_badge" />
<item type="id" name="icon1" />
<item type="id" name="icon2" />
<item type="id" name="input" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7fc2f49..202c127 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2096,6 +2096,8 @@
<public type="attr" name="windowSwipeToDismiss" id="0x10103f3" />
<public type="attr" name="isGame" id="0x10103f4" />
<public type="attr" name="allowEmbedded" id="0x10103f5" />
+ <public type="attr" name="setupActivity" id="0x10103f6"/>
+ <public type="attr" name="tvInputType" id="0x10103f7"/>
<!-- ===============================================================
Resources added in version 21 of the platform
@@ -2130,7 +2132,6 @@
<public type="attr" name="strokeLineJoin" />
<public type="attr" name="clipToPath" />
<public type="attr" name="requiredForProfile"/>
- <public type="attr" name="pinned" />
<public type="attr" name="colorControlNormal" />
<public type="attr" name="colorControlActivated" />
<public type="attr" name="colorButtonNormal" />
@@ -2141,7 +2142,6 @@
<public type="attr" name="slideEdge" />
<public type="attr" name="actionBarTheme" />
<public type="attr" name="textAppearanceListItemSecondary" />
- <public type="attr" name="colorPrimaryLight" />
<public type="attr" name="colorPrimary" />
<public type="attr" name="colorPrimaryDark" />
<public type="attr" name="colorAccent" />
@@ -2161,6 +2161,7 @@
<public type="attr" name="hideOnContentScroll" />
<public type="attr" name="actionOverflowMenuStyle" />
<public type="attr" name="documentLaunchMode" />
+ <public type="attr" name="maxRecents" />
<public type="attr" name="autoRemoveFromRecents" />
<public type="attr" name="stateListAnimator" />
<public type="attr" name="toId" />
@@ -2183,6 +2184,12 @@
<public type="attr" name="translateX" />
<public type="attr" name="translateY" />
<public type="attr" name="selectableItemBackgroundBorderless" />
+ <public type="attr" name="elegantTextHeight" />
+ <public type="attr" name="searchKeyphraseId" />
+ <public type="attr" name="searchKeyphrase" />
+ <public type="attr" name="searchKeyphraseSupportedLocales" />
+ <public type="attr" name="windowTransitionBackgroundFadeDuration" />
+ <public type="attr" name="overlapAnchor" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
@@ -2194,6 +2201,8 @@
<public type="style" name="Widget.FastScroll" />
<public type="style" name="Widget.StackView" />
+ <public type="style" name="Widget.Toolbar" />
+ <public type="style" name="Widget.Toolbar.Button.Navigation" />
<public type="style" name="Widget.Holo.FastScroll" />
<public type="style" name="Widget.Holo.StackView" />
@@ -2208,214 +2217,220 @@
<public type="style" name="Widget.DeviceDefault.Light.FastScroll" />
<public type="style" name="Widget.DeviceDefault.Light.StackView" />
- <public type="style" name="TextAppearance.Quantum" />
- <public type="style" name="TextAppearance.Quantum.DialogWindowTitle" />
- <public type="style" name="TextAppearance.Quantum.Inverse" />
- <public type="style" name="TextAppearance.Quantum.Large" />
- <public type="style" name="TextAppearance.Quantum.Large.Inverse" />
- <public type="style" name="TextAppearance.Quantum.Medium" />
- <public type="style" name="TextAppearance.Quantum.Medium.Inverse" />
- <public type="style" name="TextAppearance.Quantum.SearchResult.Subtitle" />
- <public type="style" name="TextAppearance.Quantum.SearchResult.Title" />
- <public type="style" name="TextAppearance.Quantum.Small" />
- <public type="style" name="TextAppearance.Quantum.Small.Inverse" />
- <public type="style" name="TextAppearance.Quantum.WindowTitle" />
+ <public type="style" name="TextAppearance.Material" />
+ <public type="style" name="TextAppearance.Material.DialogWindowTitle" />
+ <public type="style" name="TextAppearance.Material.Inverse" />
+ <public type="style" name="TextAppearance.Material.Large" />
+ <public type="style" name="TextAppearance.Material.Large.Inverse" />
+ <public type="style" name="TextAppearance.Material.Medium" />
+ <public type="style" name="TextAppearance.Material.Medium.Inverse" />
+ <public type="style" name="TextAppearance.Material.SearchResult.Subtitle" />
+ <public type="style" name="TextAppearance.Material.SearchResult.Title" />
+ <public type="style" name="TextAppearance.Material.Small" />
+ <public type="style" name="TextAppearance.Material.Small.Inverse" />
+ <public type="style" name="TextAppearance.Material.WindowTitle" />
- <public type="style" name="TextAppearance.Quantum.Widget" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionBar.Menu" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionBar.Subtitle" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionBar.Subtitle.Inverse" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionBar.Title" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionBar.Title.Inverse" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionMode.Subtitle" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionMode.Subtitle.Inverse" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionMode.Title" />
- <public type="style" name="TextAppearance.Quantum.Widget.ActionMode.Title.Inverse" />
- <public type="style" name="TextAppearance.Quantum.Widget.Button" />
- <public type="style" name="TextAppearance.Quantum.Widget.DropDownHint" />
- <public type="style" name="TextAppearance.Quantum.Widget.DropDownItem" />
- <public type="style" name="TextAppearance.Quantum.Widget.EditText" />
- <public type="style" name="TextAppearance.Quantum.Widget.IconMenu.Item" />
- <public type="style" name="TextAppearance.Quantum.Widget.PopupMenu" />
- <public type="style" name="TextAppearance.Quantum.Widget.PopupMenu.Large" />
- <public type="style" name="TextAppearance.Quantum.Widget.PopupMenu.Small" />
- <public type="style" name="TextAppearance.Quantum.Widget.TabWidget" />
- <public type="style" name="TextAppearance.Quantum.Widget.TextView" />
- <public type="style" name="TextAppearance.Quantum.Widget.TextView.PopupMenu" />
- <public type="style" name="TextAppearance.Quantum.Widget.TextView.SpinnerItem" />
+ <public type="style" name="TextAppearance.Material.Widget" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionBar.Menu" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionBar.Subtitle" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionBar.Subtitle.Inverse" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionBar.Title" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionBar.Title.Inverse" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionMode.Subtitle" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionMode.Title" />
+ <public type="style" name="TextAppearance.Material.Widget.ActionMode.Title.Inverse" />
+ <public type="style" name="TextAppearance.Material.Widget.Button" />
+ <public type="style" name="TextAppearance.Material.Widget.DropDownHint" />
+ <public type="style" name="TextAppearance.Material.Widget.DropDownItem" />
+ <public type="style" name="TextAppearance.Material.Widget.EditText" />
+ <public type="style" name="TextAppearance.Material.Widget.IconMenu.Item" />
+ <public type="style" name="TextAppearance.Material.Widget.PopupMenu" />
+ <public type="style" name="TextAppearance.Material.Widget.PopupMenu.Large" />
+ <public type="style" name="TextAppearance.Material.Widget.PopupMenu.Small" />
+ <public type="style" name="TextAppearance.Material.Widget.TabWidget" />
+ <public type="style" name="TextAppearance.Material.Widget.TextView" />
+ <public type="style" name="TextAppearance.Material.Widget.TextView.PopupMenu" />
+ <public type="style" name="TextAppearance.Material.Widget.TextView.SpinnerItem" />
- <public type="style" name="Theme.Quantum" />
- <public type="style" name="Theme.Quantum.Dialog" />
- <public type="style" name="Theme.Quantum.Dialog.MinWidth" />
- <public type="style" name="Theme.Quantum.Dialog.NoActionBar" />
- <public type="style" name="Theme.Quantum.Dialog.NoActionBar.MinWidth" />
- <public type="style" name="Theme.Quantum.DialogWhenLarge" />
- <public type="style" name="Theme.Quantum.DialogWhenLarge.NoActionBar" />
- <public type="style" name="Theme.Quantum.InputMethod" />
- <public type="style" name="Theme.Quantum.NoActionBar" />
- <public type="style" name="Theme.Quantum.NoActionBar.Fullscreen" />
- <public type="style" name="Theme.Quantum.NoActionBar.Overscan" />
- <public type="style" name="Theme.Quantum.NoActionBar.TranslucentDecor" />
- <public type="style" name="Theme.Quantum.Panel" />
- <public type="style" name="Theme.Quantum.Voice" />
- <public type="style" name="Theme.Quantum.Wallpaper" />
- <public type="style" name="Theme.Quantum.Wallpaper.NoTitleBar" />
+ <public type="style" name="Theme.DeviceDefault.Settings" />
- <public type="style" name="Theme.Quantum.Light" />
- <public type="style" name="Theme.Quantum.Light.DarkActionBar" />
- <public type="style" name="Theme.Quantum.Light.Dialog" />
- <public type="style" name="Theme.Quantum.Light.Dialog.MinWidth" />
- <public type="style" name="Theme.Quantum.Light.Dialog.NoActionBar" />
- <public type="style" name="Theme.Quantum.Light.Dialog.NoActionBar.MinWidth" />
- <public type="style" name="Theme.Quantum.Light.DialogWhenLarge" />
- <public type="style" name="Theme.Quantum.Light.DialogWhenLarge.NoActionBar" />
- <public type="style" name="Theme.Quantum.Light.NoActionBar" />
- <public type="style" name="Theme.Quantum.Light.NoActionBar.Fullscreen" />
- <public type="style" name="Theme.Quantum.Light.NoActionBar.Overscan" />
- <public type="style" name="Theme.Quantum.Light.NoActionBar.TranslucentDecor" />
- <public type="style" name="Theme.Quantum.Light.Panel" />
- <public type="style" name="Theme.Quantum.Light.Voice" />
+ <public type="style" name="Theme.Material" />
+ <public type="style" name="Theme.Material.Dialog" />
+ <public type="style" name="Theme.Material.Dialog.MinWidth" />
+ <public type="style" name="Theme.Material.Dialog.NoActionBar" />
+ <public type="style" name="Theme.Material.Dialog.NoActionBar.MinWidth" />
+ <public type="style" name="Theme.Material.DialogWhenLarge" />
+ <public type="style" name="Theme.Material.DialogWhenLarge.NoActionBar" />
+ <public type="style" name="Theme.Material.InputMethod" />
+ <public type="style" name="Theme.Material.NoActionBar" />
+ <public type="style" name="Theme.Material.NoActionBar.Fullscreen" />
+ <public type="style" name="Theme.Material.NoActionBar.Overscan" />
+ <public type="style" name="Theme.Material.NoActionBar.TranslucentDecor" />
+ <public type="style" name="Theme.Material.Panel" />
+ <public type="style" name="Theme.Material.Settings" />
+ <public type="style" name="Theme.Material.Voice" />
+ <public type="style" name="Theme.Material.Wallpaper" />
+ <public type="style" name="Theme.Material.Wallpaper.NoTitleBar" />
+
+ <public type="style" name="Theme.Material.Light" />
+ <public type="style" name="Theme.Material.Light.DarkActionBar" />
+ <public type="style" name="Theme.Material.Light.Dialog" />
+ <public type="style" name="Theme.Material.Light.Dialog.MinWidth" />
+ <public type="style" name="Theme.Material.Light.Dialog.NoActionBar" />
+ <public type="style" name="Theme.Material.Light.Dialog.NoActionBar.MinWidth" />
+ <public type="style" name="Theme.Material.Light.DialogWhenLarge" />
+ <public type="style" name="Theme.Material.Light.DialogWhenLarge.NoActionBar" />
+ <public type="style" name="Theme.Material.Light.NoActionBar" />
+ <public type="style" name="Theme.Material.Light.NoActionBar.Fullscreen" />
+ <public type="style" name="Theme.Material.Light.NoActionBar.Overscan" />
+ <public type="style" name="Theme.Material.Light.NoActionBar.TranslucentDecor" />
+ <public type="style" name="Theme.Material.Light.Panel" />
+ <public type="style" name="Theme.Material.Light.Voice" />
<public type="style" name="ThemeOverlay" />
- <public type="style" name="ThemeOverlay.Quantum" />
- <public type="style" name="ThemeOverlay.Quantum.Light" />
- <public type="style" name="ThemeOverlay.Quantum.Dark" />
- <public type="style" name="ThemeOverlay.Quantum.ActionBarWidget" />
+ <public type="style" name="ThemeOverlay.Material" />
+ <public type="style" name="ThemeOverlay.Material.ActionBar" />
+ <public type="style" name="ThemeOverlay.Material.Light" />
+ <public type="style" name="ThemeOverlay.Material.Dark" />
+ <public type="style" name="ThemeOverlay.Material.Dark.ActionBar" />
- <public type="style" name="Widget.Quantum" />
- <public type="style" name="Widget.Quantum.ActionBar" />
- <public type="style" name="Widget.Quantum.ActionBar.Solid" />
- <public type="style" name="Widget.Quantum.ActionBar.TabBar" />
- <public type="style" name="Widget.Quantum.ActionBar.TabText" />
- <public type="style" name="Widget.Quantum.ActionBar.TabView" />
- <public type="style" name="Widget.Quantum.ActionButton" />
- <public type="style" name="Widget.Quantum.ActionButton.CloseMode" />
- <public type="style" name="Widget.Quantum.ActionButton.Overflow" />
- <public type="style" name="Widget.Quantum.ActionMode" />
- <public type="style" name="Widget.Quantum.AutoCompleteTextView" />
- <public type="style" name="Widget.Quantum.Button" />
- <public type="style" name="Widget.Quantum.Button.Borderless" />
- <public type="style" name="Widget.Quantum.Button.Borderless.Small" />
- <public type="style" name="Widget.Quantum.Button.Inset" />
- <public type="style" name="Widget.Quantum.Button.Small" />
- <public type="style" name="Widget.Quantum.Button.Toggle" />
- <public type="style" name="Widget.Quantum.ButtonBar" />
- <public type="style" name="Widget.Quantum.ButtonBar.AlertDialog" />
- <public type="style" name="Widget.Quantum.CalendarView" />
- <public type="style" name="Widget.Quantum.CheckedTextView" />
- <public type="style" name="Widget.Quantum.CompoundButton.CheckBox" />
- <public type="style" name="Widget.Quantum.CompoundButton.RadioButton" />
- <public type="style" name="Widget.Quantum.CompoundButton.Star" />
- <public type="style" name="Widget.Quantum.DatePicker" />
- <public type="style" name="Widget.Quantum.DropDownItem" />
- <public type="style" name="Widget.Quantum.DropDownItem.Spinner" />
- <public type="style" name="Widget.Quantum.EditText" />
- <public type="style" name="Widget.Quantum.ExpandableListView" />
- <public type="style" name="Widget.Quantum.FastScroll" />
- <public type="style" name="Widget.Quantum.GridView" />
- <public type="style" name="Widget.Quantum.HorizontalScrollView" />
- <public type="style" name="Widget.Quantum.ImageButton" />
- <public type="style" name="Widget.Quantum.ListPopupWindow" />
- <public type="style" name="Widget.Quantum.ListView" />
- <public type="style" name="Widget.Quantum.ListView.DropDown" />
- <public type="style" name="Widget.Quantum.MediaRouteButton" />
- <public type="style" name="Widget.Quantum.PopupMenu" />
- <public type="style" name="Widget.Quantum.PopupMenu.Overflow" />
- <public type="style" name="Widget.Quantum.PopupWindow" />
- <public type="style" name="Widget.Quantum.ProgressBar" />
- <public type="style" name="Widget.Quantum.ProgressBar.Horizontal" />
- <public type="style" name="Widget.Quantum.ProgressBar.Large" />
- <public type="style" name="Widget.Quantum.ProgressBar.Small" />
- <public type="style" name="Widget.Quantum.ProgressBar.Small.Title" />
- <public type="style" name="Widget.Quantum.RatingBar" />
- <public type="style" name="Widget.Quantum.RatingBar.Indicator" />
- <public type="style" name="Widget.Quantum.RatingBar.Small" />
- <public type="style" name="Widget.Quantum.ScrollView" />
- <public type="style" name="Widget.Quantum.SeekBar" />
- <public type="style" name="Widget.Quantum.SegmentedButton" />
- <public type="style" name="Widget.Quantum.StackView" />
- <public type="style" name="Widget.Quantum.Spinner" />
- <public type="style" name="Widget.Quantum.Tab" />
- <public type="style" name="Widget.Quantum.TabWidget" />
- <public type="style" name="Widget.Quantum.TextView" />
- <public type="style" name="Widget.Quantum.TextView.SpinnerItem" />
- <public type="style" name="Widget.Quantum.WebTextView" />
- <public type="style" name="Widget.Quantum.WebView" />
+ <public type="style" name="Widget.Material" />
+ <public type="style" name="Widget.Material.ActionBar" />
+ <public type="style" name="Widget.Material.ActionBar.Solid" />
+ <public type="style" name="Widget.Material.ActionBar.TabBar" />
+ <public type="style" name="Widget.Material.ActionBar.TabText" />
+ <public type="style" name="Widget.Material.ActionBar.TabView" />
+ <public type="style" name="Widget.Material.ActionButton" />
+ <public type="style" name="Widget.Material.ActionButton.CloseMode" />
+ <public type="style" name="Widget.Material.ActionButton.Overflow" />
+ <public type="style" name="Widget.Material.ActionMode" />
+ <public type="style" name="Widget.Material.AutoCompleteTextView" />
+ <public type="style" name="Widget.Material.Button" />
+ <public type="style" name="Widget.Material.Button.Borderless" />
+ <public type="style" name="Widget.Material.Button.Borderless.Small" />
+ <public type="style" name="Widget.Material.Button.Inset" />
+ <public type="style" name="Widget.Material.Button.Small" />
+ <public type="style" name="Widget.Material.Button.Toggle" />
+ <public type="style" name="Widget.Material.ButtonBar" />
+ <public type="style" name="Widget.Material.ButtonBar.AlertDialog" />
+ <public type="style" name="Widget.Material.CalendarView" />
+ <public type="style" name="Widget.Material.CheckedTextView" />
+ <public type="style" name="Widget.Material.CompoundButton.CheckBox" />
+ <public type="style" name="Widget.Material.CompoundButton.RadioButton" />
+ <public type="style" name="Widget.Material.CompoundButton.Star" />
+ <public type="style" name="Widget.Material.DatePicker" />
+ <public type="style" name="Widget.Material.DropDownItem" />
+ <public type="style" name="Widget.Material.DropDownItem.Spinner" />
+ <public type="style" name="Widget.Material.EditText" />
+ <public type="style" name="Widget.Material.ExpandableListView" />
+ <public type="style" name="Widget.Material.FastScroll" />
+ <public type="style" name="Widget.Material.GridView" />
+ <public type="style" name="Widget.Material.HorizontalScrollView" />
+ <public type="style" name="Widget.Material.ImageButton" />
+ <public type="style" name="Widget.Material.ListPopupWindow" />
+ <public type="style" name="Widget.Material.ListView" />
+ <public type="style" name="Widget.Material.ListView.DropDown" />
+ <public type="style" name="Widget.Material.MediaRouteButton" />
+ <public type="style" name="Widget.Material.PopupMenu" />
+ <public type="style" name="Widget.Material.PopupMenu.Overflow" />
+ <public type="style" name="Widget.Material.PopupWindow" />
+ <public type="style" name="Widget.Material.ProgressBar" />
+ <public type="style" name="Widget.Material.ProgressBar.Horizontal" />
+ <public type="style" name="Widget.Material.ProgressBar.Large" />
+ <public type="style" name="Widget.Material.ProgressBar.Small" />
+ <public type="style" name="Widget.Material.ProgressBar.Small.Title" />
+ <public type="style" name="Widget.Material.RatingBar" />
+ <public type="style" name="Widget.Material.RatingBar.Indicator" />
+ <public type="style" name="Widget.Material.RatingBar.Small" />
+ <public type="style" name="Widget.Material.ScrollView" />
+ <public type="style" name="Widget.Material.SeekBar" />
+ <public type="style" name="Widget.Material.SegmentedButton" />
+ <public type="style" name="Widget.Material.StackView" />
+ <public type="style" name="Widget.Material.Spinner" />
+ <public type="style" name="Widget.Material.Tab" />
+ <public type="style" name="Widget.Material.TabWidget" />
+ <public type="style" name="Widget.Material.TextView" />
+ <public type="style" name="Widget.Material.TextView.SpinnerItem" />
+ <public type="style" name="Widget.Material.Toolbar" />
+ <public type="style" name="Widget.Material.Toolbar.Button.Navigation" />
+ <public type="style" name="Widget.Material.WebTextView" />
+ <public type="style" name="Widget.Material.WebView" />
- <public type="style" name="Widget.Quantum.Light" />
- <public type="style" name="Widget.Quantum.Light.ActionBar" />
- <public type="style" name="Widget.Quantum.Light.ActionBar.Solid" />
- <public type="style" name="Widget.Quantum.Light.ActionBar.TabBar" />
- <public type="style" name="Widget.Quantum.Light.ActionBar.TabText" />
- <public type="style" name="Widget.Quantum.Light.ActionBar.TabView" />
- <public type="style" name="Widget.Quantum.Light.ActionButton" />
- <public type="style" name="Widget.Quantum.Light.ActionButton.CloseMode" />
- <public type="style" name="Widget.Quantum.Light.ActionButton.Overflow" />
- <public type="style" name="Widget.Quantum.Light.ActionMode" />
- <public type="style" name="Widget.Quantum.Light.AutoCompleteTextView" />
- <public type="style" name="Widget.Quantum.Light.Button" />
- <public type="style" name="Widget.Quantum.Light.Button.Borderless" />
- <public type="style" name="Widget.Quantum.Light.Button.Borderless.Small" />
- <public type="style" name="Widget.Quantum.Light.Button.Inset" />
- <public type="style" name="Widget.Quantum.Light.Button.Small" />
- <public type="style" name="Widget.Quantum.Light.Button.Toggle" />
- <public type="style" name="Widget.Quantum.Light.ButtonBar" />
- <public type="style" name="Widget.Quantum.Light.ButtonBar.AlertDialog" />
- <public type="style" name="Widget.Quantum.Light.CalendarView" />
- <public type="style" name="Widget.Quantum.Light.CheckedTextView" />
- <public type="style" name="Widget.Quantum.Light.CompoundButton.CheckBox" />
- <public type="style" name="Widget.Quantum.Light.CompoundButton.RadioButton" />
- <public type="style" name="Widget.Quantum.Light.CompoundButton.Star" />
- <public type="style" name="Widget.Quantum.Light.DropDownItem" />
- <public type="style" name="Widget.Quantum.Light.DropDownItem.Spinner" />
- <public type="style" name="Widget.Quantum.Light.EditText" />
- <public type="style" name="Widget.Quantum.Light.ExpandableListView" />
- <public type="style" name="Widget.Quantum.Light.FastScroll" />
- <public type="style" name="Widget.Quantum.Light.GridView" />
- <public type="style" name="Widget.Quantum.Light.HorizontalScrollView" />
- <public type="style" name="Widget.Quantum.Light.ImageButton" />
- <public type="style" name="Widget.Quantum.Light.ListPopupWindow" />
- <public type="style" name="Widget.Quantum.Light.ListView" />
- <public type="style" name="Widget.Quantum.Light.ListView.DropDown" />
- <public type="style" name="Widget.Quantum.Light.MediaRouteButton" />
- <public type="style" name="Widget.Quantum.Light.PopupMenu" />
- <public type="style" name="Widget.Quantum.Light.PopupMenu.Overflow" />
- <public type="style" name="Widget.Quantum.Light.PopupWindow" />
- <public type="style" name="Widget.Quantum.Light.ProgressBar" />
- <public type="style" name="Widget.Quantum.Light.ProgressBar.Horizontal" />
- <public type="style" name="Widget.Quantum.Light.ProgressBar.Inverse" />
- <public type="style" name="Widget.Quantum.Light.ProgressBar.Large" />
- <public type="style" name="Widget.Quantum.Light.ProgressBar.Large.Inverse" />
- <public type="style" name="Widget.Quantum.Light.ProgressBar.Small" />
- <public type="style" name="Widget.Quantum.Light.ProgressBar.Small.Inverse" />
- <public type="style" name="Widget.Quantum.Light.ProgressBar.Small.Title" />
- <public type="style" name="Widget.Quantum.Light.RatingBar" />
- <public type="style" name="Widget.Quantum.Light.RatingBar.Indicator" />
- <public type="style" name="Widget.Quantum.Light.RatingBar.Small" />
- <public type="style" name="Widget.Quantum.Light.ScrollView" />
- <public type="style" name="Widget.Quantum.Light.SeekBar" />
- <public type="style" name="Widget.Quantum.Light.SegmentedButton" />
- <public type="style" name="Widget.Quantum.Light.StackView" />
- <public type="style" name="Widget.Quantum.Light.Spinner" />
- <public type="style" name="Widget.Quantum.Light.Tab" />
- <public type="style" name="Widget.Quantum.Light.TabWidget" />
- <public type="style" name="Widget.Quantum.Light.TextView" />
- <public type="style" name="Widget.Quantum.Light.TextView.SpinnerItem" />
- <public type="style" name="Widget.Quantum.Light.WebTextView" />
- <public type="style" name="Widget.Quantum.Light.WebView" />
+ <public type="style" name="Widget.Material.Light" />
+ <public type="style" name="Widget.Material.Light.ActionBar" />
+ <public type="style" name="Widget.Material.Light.ActionBar.Solid" />
+ <public type="style" name="Widget.Material.Light.ActionBar.TabBar" />
+ <public type="style" name="Widget.Material.Light.ActionBar.TabText" />
+ <public type="style" name="Widget.Material.Light.ActionBar.TabView" />
+ <public type="style" name="Widget.Material.Light.ActionButton" />
+ <public type="style" name="Widget.Material.Light.ActionButton.CloseMode" />
+ <public type="style" name="Widget.Material.Light.ActionButton.Overflow" />
+ <public type="style" name="Widget.Material.Light.ActionMode" />
+ <public type="style" name="Widget.Material.Light.AutoCompleteTextView" />
+ <public type="style" name="Widget.Material.Light.Button" />
+ <public type="style" name="Widget.Material.Light.Button.Borderless" />
+ <public type="style" name="Widget.Material.Light.Button.Borderless.Small" />
+ <public type="style" name="Widget.Material.Light.Button.Inset" />
+ <public type="style" name="Widget.Material.Light.Button.Small" />
+ <public type="style" name="Widget.Material.Light.Button.Toggle" />
+ <public type="style" name="Widget.Material.Light.ButtonBar" />
+ <public type="style" name="Widget.Material.Light.ButtonBar.AlertDialog" />
+ <public type="style" name="Widget.Material.Light.CalendarView" />
+ <public type="style" name="Widget.Material.Light.CheckedTextView" />
+ <public type="style" name="Widget.Material.Light.CompoundButton.CheckBox" />
+ <public type="style" name="Widget.Material.Light.CompoundButton.RadioButton" />
+ <public type="style" name="Widget.Material.Light.CompoundButton.Star" />
+ <public type="style" name="Widget.Material.Light.DropDownItem" />
+ <public type="style" name="Widget.Material.Light.DropDownItem.Spinner" />
+ <public type="style" name="Widget.Material.Light.EditText" />
+ <public type="style" name="Widget.Material.Light.ExpandableListView" />
+ <public type="style" name="Widget.Material.Light.FastScroll" />
+ <public type="style" name="Widget.Material.Light.GridView" />
+ <public type="style" name="Widget.Material.Light.HorizontalScrollView" />
+ <public type="style" name="Widget.Material.Light.ImageButton" />
+ <public type="style" name="Widget.Material.Light.ListPopupWindow" />
+ <public type="style" name="Widget.Material.Light.ListView" />
+ <public type="style" name="Widget.Material.Light.ListView.DropDown" />
+ <public type="style" name="Widget.Material.Light.MediaRouteButton" />
+ <public type="style" name="Widget.Material.Light.PopupMenu" />
+ <public type="style" name="Widget.Material.Light.PopupMenu.Overflow" />
+ <public type="style" name="Widget.Material.Light.PopupWindow" />
+ <public type="style" name="Widget.Material.Light.ProgressBar" />
+ <public type="style" name="Widget.Material.Light.ProgressBar.Horizontal" />
+ <public type="style" name="Widget.Material.Light.ProgressBar.Inverse" />
+ <public type="style" name="Widget.Material.Light.ProgressBar.Large" />
+ <public type="style" name="Widget.Material.Light.ProgressBar.Large.Inverse" />
+ <public type="style" name="Widget.Material.Light.ProgressBar.Small" />
+ <public type="style" name="Widget.Material.Light.ProgressBar.Small.Inverse" />
+ <public type="style" name="Widget.Material.Light.ProgressBar.Small.Title" />
+ <public type="style" name="Widget.Material.Light.RatingBar" />
+ <public type="style" name="Widget.Material.Light.RatingBar.Indicator" />
+ <public type="style" name="Widget.Material.Light.RatingBar.Small" />
+ <public type="style" name="Widget.Material.Light.ScrollView" />
+ <public type="style" name="Widget.Material.Light.SeekBar" />
+ <public type="style" name="Widget.Material.Light.SegmentedButton" />
+ <public type="style" name="Widget.Material.Light.StackView" />
+ <public type="style" name="Widget.Material.Light.Spinner" />
+ <public type="style" name="Widget.Material.Light.Tab" />
+ <public type="style" name="Widget.Material.Light.TabWidget" />
+ <public type="style" name="Widget.Material.Light.TextView" />
+ <public type="style" name="Widget.Material.Light.TextView.SpinnerItem" />
+ <public type="style" name="Widget.Material.Light.WebTextView" />
+ <public type="style" name="Widget.Material.Light.WebView" />
- <public type="style" name="TextAppearance.Quantum.Display4" />
- <public type="style" name="TextAppearance.Quantum.Display3" />
- <public type="style" name="TextAppearance.Quantum.Display2" />
- <public type="style" name="TextAppearance.Quantum.Display1" />
- <public type="style" name="TextAppearance.Quantum.Headline" />
- <public type="style" name="TextAppearance.Quantum.Title" />
- <public type="style" name="TextAppearance.Quantum.Subhead" />
- <public type="style" name="TextAppearance.Quantum.Body2" />
- <public type="style" name="TextAppearance.Quantum.Body1" />
- <public type="style" name="TextAppearance.Quantum.Caption" />
- <public type="style" name="TextAppearance.Quantum.Menu" />
- <public type="style" name="TextAppearance.Quantum.Button" />
+ <public type="style" name="TextAppearance.Material.Display4" />
+ <public type="style" name="TextAppearance.Material.Display3" />
+ <public type="style" name="TextAppearance.Material.Display2" />
+ <public type="style" name="TextAppearance.Material.Display1" />
+ <public type="style" name="TextAppearance.Material.Headline" />
+ <public type="style" name="TextAppearance.Material.Title" />
+ <public type="style" name="TextAppearance.Material.Subhead" />
+ <public type="style" name="TextAppearance.Material.Body2" />
+ <public type="style" name="TextAppearance.Material.Body1" />
+ <public type="style" name="TextAppearance.Material.Caption" />
+ <public type="style" name="TextAppearance.Material.Menu" />
+ <public type="style" name="TextAppearance.Material.Button" />
<public-padding type="interpolator" name="l_resource_pad" end="0x010c0010" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7234911..0ddfeb6 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -457,10 +457,10 @@
<string name="android_system_label">Android System</string>
<!-- Label for the user owner in the intent forwarding app. -->
- <string name="user_owner_label">Personal</string>
+ <string name="user_owner_label">Personal apps</string>
<!-- Label for a corporate profile in the intent forwarding app. -->
- <string name="managed_profile_label">Work</string>
+ <string name="managed_profile_label">Android Work</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_costMoney">Services that cost you money</string>
@@ -1122,6 +1122,12 @@
interface of a voice interaction service. Should never be needed for normal apps.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_manageVoiceKeyphrases">manage voice keyphrases</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_manageVoiceKeyphrases">Allows the holder to manage the keyphrases for voice hotword detection.
+ Should never be needed for normal apps.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_bindRemoteDisplay">bind to a remote display</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_bindRemoteDisplay">Allows the holder to bind to the top-level
@@ -1260,11 +1266,11 @@
<!-- Title of a permission that is never presented to the user. This is not a
permission that an application must be granted by the user. Instead, it
is part of a mechanism that applications use to indicate to the system
- that they want to do occasional work while the device is idle. -->
- <string name="permlab_bindIdleService">run application during idle time</string>
+ that they want to do scheduled background work. -->
+ <string name="permlab_bindJobService">run the application\'s scheduled background work</string>
<!-- Description of an application permission, so that the user can understand
what is being done if they are curious. -->
- <string name="permdesc_bindIdleService">This permission allows the Android system to run the application in the background while the device is not in use.</string>
+ <string name="permdesc_bindJobService">This permission allows the Android system to run the application in the background when requested.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_diagnostic">read/write to resources owned by diag</string>
@@ -1556,6 +1562,12 @@
happen in the background but does not prevent other audio capture (e.g. Camcorder).</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_modifyAudioRouting">Audio Routing</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_modifyAudioRouting">Allows the app to directly control audio routing and
+ override audio policy decisions.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_captureVideoOutput">capture video output</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_captureVideoOutput">Allows the app to capture and redirect video output.</string>
@@ -3779,6 +3791,11 @@
<!-- Description of an application permission that lets it listen to trust state changes. -->
<string name="permdesc_trust_listener">Allows an application to listen for changes in trust state.</string>
+ <!-- Title of an application permission that lets it provide a trust agent. -->
+ <string name="permlab_provide_trust_agent">Provide a trust agent.</string>
+ <!-- Description of an application permission that lets it provide a trust agent. -->
+ <string name="permdesc_provide_trust_agent">Allows an application to provide a trust agent.</string>
+
<!-- Title of an application permission that lets it bind to a trust agent service. -->
<string name="permlab_bind_trust_agent_service">Bind to a trust agent service</string>
<!-- Description of an application permission that lets it bind to a trust agent service. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 59bd667..5bd6122 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1199,6 +1199,7 @@
<item name="android:buttonGravity">top</item>
<item name="android:navigationButtonStyle">@android:style/Widget.Toolbar.Button.Navigation</item>
<item name="android:collapseIcon">?android:attr/homeAsUpIndicator</item>
+ <item name="android:contentInsetStart">16dp</item>
</style>
<style name="Widget.Toolbar.Button.Navigation" parent="@android:style/Widget">
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 609a0f3..84dbc79 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -32,149 +32,149 @@
-->
<resources>
<!-- Widget Styles -->
- <style name="Widget.DeviceDefault" parent="Widget.Quantum"/>
- <style name="Widget.DeviceDefault.Button" parent="Widget.Quantum.Button"/>
- <style name="Widget.DeviceDefault.Button.Small" parent="Widget.Quantum.Button.Small"/>
- <style name="Widget.DeviceDefault.Button.Inset" parent="Widget.Quantum.Button.Inset"/>
- <style name="Widget.DeviceDefault.Button.Toggle" parent="Widget.Quantum.Button.Toggle"/>
- <style name="Widget.DeviceDefault.TextView" parent="Widget.Quantum.TextView"/>
- <style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Quantum.CheckedTextView"/>
- <style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Quantum.AutoCompleteTextView"/>
- <style name="Widget.DeviceDefault.CompoundButton.CheckBox" parent="Widget.Quantum.CompoundButton.CheckBox"/>
- <style name="Widget.DeviceDefault.ListView.DropDown" parent="Widget.Quantum.ListView.DropDown"/>
- <style name="Widget.DeviceDefault.EditText" parent="Widget.Quantum.EditText"/>
- <style name="Widget.DeviceDefault.ExpandableListView" parent="Widget.Quantum.ExpandableListView"/>
- <style name="Widget.DeviceDefault.GridView" parent="Widget.Quantum.GridView"/>
- <style name="Widget.DeviceDefault.ImageButton" parent="Widget.Quantum.ImageButton"/>
- <style name="Widget.DeviceDefault.ListView" parent="Widget.Quantum.ListView"/>
- <style name="Widget.DeviceDefault.PopupWindow" parent="Widget.Quantum.PopupWindow"/>
- <style name="Widget.DeviceDefault.ProgressBar" parent="Widget.Quantum.ProgressBar"/>
- <style name="Widget.DeviceDefault.ProgressBar.Horizontal" parent="Widget.Quantum.ProgressBar.Horizontal"/>
- <style name="Widget.DeviceDefault.ProgressBar.Small" parent="Widget.Quantum.ProgressBar.Small"/>
- <style name="Widget.DeviceDefault.ProgressBar.Small.Title" parent="Widget.Quantum.ProgressBar.Small.Title"/>
- <style name="Widget.DeviceDefault.ProgressBar.Large" parent="Widget.Quantum.ProgressBar.Large"/>
- <style name="Widget.DeviceDefault.SeekBar" parent="Widget.Quantum.SeekBar"/>
- <style name="Widget.DeviceDefault.RatingBar" parent="Widget.Quantum.RatingBar"/>
- <style name="Widget.DeviceDefault.RatingBar.Indicator" parent="Widget.Quantum.RatingBar.Indicator"/>
- <style name="Widget.DeviceDefault.RatingBar.Small" parent="Widget.Quantum.RatingBar.Small"/>
- <style name="Widget.DeviceDefault.CompoundButton.RadioButton" parent="Widget.Quantum.CompoundButton.RadioButton"/>
- <style name="Widget.DeviceDefault.ScrollView" parent="Widget.Quantum.ScrollView"/>
- <style name="Widget.DeviceDefault.HorizontalScrollView" parent="Widget.Quantum.HorizontalScrollView"/>
- <style name="Widget.DeviceDefault.Spinner" parent="Widget.Quantum.Spinner"/>
- <style name="Widget.DeviceDefault.CompoundButton.Star" parent="Widget.Quantum.CompoundButton.Star"/>
- <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Quantum.TabWidget"/>
- <style name="Widget.DeviceDefault.WebTextView" parent="Widget.Quantum.WebTextView"/>
- <style name="Widget.DeviceDefault.WebView" parent="Widget.Quantum.WebView"/>
- <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Quantum.DropDownItem"/>
- <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.Quantum.DropDownItem.Spinner"/>
- <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Quantum.TextView.SpinnerItem"/>
- <style name="Widget.DeviceDefault.ListPopupWindow" parent="Widget.Quantum.ListPopupWindow"/>
- <style name="Widget.DeviceDefault.PopupMenu" parent="Widget.Quantum.PopupMenu"/>
- <style name="Widget.DeviceDefault.ActionButton" parent="Widget.Quantum.ActionButton"/>
- <style name="Widget.DeviceDefault.ActionButton.Overflow" parent="Widget.Quantum.ActionButton.Overflow"/>
- <style name="Widget.DeviceDefault.ActionButton.TextButton" parent="Widget.Quantum.ActionButton"/>
- <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Quantum.ActionMode"/>
- <style name="Widget.DeviceDefault.ActionButton.CloseMode" parent="Widget.Quantum.ActionButton.CloseMode"/>
- <style name="Widget.DeviceDefault.ActionBar" parent="Widget.Quantum.ActionBar"/>
- <style name="Widget.DeviceDefault.Button.Borderless" parent="Widget.Quantum.Button.Borderless"/>
- <style name="Widget.DeviceDefault.Tab" parent="Widget.Quantum.Tab"/>
- <style name="Widget.DeviceDefault.CalendarView" parent="Widget.Quantum.CalendarView"/>
- <style name="Widget.DeviceDefault.DatePicker" parent="Widget.Quantum.DatePicker"/>
- <style name="Widget.DeviceDefault.ActionBar.TabView" parent="Widget.Quantum.ActionBar.TabView"/>
- <style name="Widget.DeviceDefault.ActionBar.TabText" parent="Widget.Quantum.ActionBar.TabText"/>
- <style name="Widget.DeviceDefault.ActionBar.TabBar" parent="Widget.Quantum.ActionBar.TabBar"/>
- <style name="Widget.DeviceDefault.ActionBar.Solid" parent="Widget.Quantum.ActionBar.Solid"/>
- <style name="Widget.DeviceDefault.Button.Borderless.Small" parent="Widget.Quantum.Button.Borderless.Small"/>
- <style name="Widget.DeviceDefault.AbsListView" parent="Widget.Quantum.AbsListView"/>
- <style name="Widget.DeviceDefault.Spinner.DropDown.ActionBar" parent="Widget.Quantum.Spinner.DropDown.ActionBar"/>
- <style name="Widget.DeviceDefault.PopupWindow.ActionMode" parent="Widget.Quantum.PopupWindow.ActionMode"/>
- <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Quantum.CompoundButton.Switch"/>
- <style name="Widget.DeviceDefault.ExpandableListView.White" parent="Widget.Quantum.ExpandableListView.White"/>
- <style name="Widget.DeviceDefault.FastScroll" parent="Widget.Quantum.FastScroll"/>
+ <style name="Widget.DeviceDefault" parent="Widget.Material"/>
+ <style name="Widget.DeviceDefault.Button" parent="Widget.Material.Button"/>
+ <style name="Widget.DeviceDefault.Button.Small" parent="Widget.Material.Button.Small"/>
+ <style name="Widget.DeviceDefault.Button.Inset" parent="Widget.Material.Button.Inset"/>
+ <style name="Widget.DeviceDefault.Button.Toggle" parent="Widget.Material.Button.Toggle"/>
+ <style name="Widget.DeviceDefault.TextView" parent="Widget.Material.TextView"/>
+ <style name="Widget.DeviceDefault.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
+ <style name="Widget.DeviceDefault.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView"/>
+ <style name="Widget.DeviceDefault.CompoundButton.CheckBox" parent="Widget.Material.CompoundButton.CheckBox"/>
+ <style name="Widget.DeviceDefault.ListView.DropDown" parent="Widget.Material.ListView.DropDown"/>
+ <style name="Widget.DeviceDefault.EditText" parent="Widget.Material.EditText"/>
+ <style name="Widget.DeviceDefault.ExpandableListView" parent="Widget.Material.ExpandableListView"/>
+ <style name="Widget.DeviceDefault.GridView" parent="Widget.Material.GridView"/>
+ <style name="Widget.DeviceDefault.ImageButton" parent="Widget.Material.ImageButton"/>
+ <style name="Widget.DeviceDefault.ListView" parent="Widget.Material.ListView"/>
+ <style name="Widget.DeviceDefault.PopupWindow" parent="Widget.Material.PopupWindow"/>
+ <style name="Widget.DeviceDefault.ProgressBar" parent="Widget.Material.ProgressBar"/>
+ <style name="Widget.DeviceDefault.ProgressBar.Horizontal" parent="Widget.Material.ProgressBar.Horizontal"/>
+ <style name="Widget.DeviceDefault.ProgressBar.Small" parent="Widget.Material.ProgressBar.Small"/>
+ <style name="Widget.DeviceDefault.ProgressBar.Small.Title" parent="Widget.Material.ProgressBar.Small.Title"/>
+ <style name="Widget.DeviceDefault.ProgressBar.Large" parent="Widget.Material.ProgressBar.Large"/>
+ <style name="Widget.DeviceDefault.SeekBar" parent="Widget.Material.SeekBar"/>
+ <style name="Widget.DeviceDefault.RatingBar" parent="Widget.Material.RatingBar"/>
+ <style name="Widget.DeviceDefault.RatingBar.Indicator" parent="Widget.Material.RatingBar.Indicator"/>
+ <style name="Widget.DeviceDefault.RatingBar.Small" parent="Widget.Material.RatingBar.Small"/>
+ <style name="Widget.DeviceDefault.CompoundButton.RadioButton" parent="Widget.Material.CompoundButton.RadioButton"/>
+ <style name="Widget.DeviceDefault.ScrollView" parent="Widget.Material.ScrollView"/>
+ <style name="Widget.DeviceDefault.HorizontalScrollView" parent="Widget.Material.HorizontalScrollView"/>
+ <style name="Widget.DeviceDefault.Spinner" parent="Widget.Material.Spinner"/>
+ <style name="Widget.DeviceDefault.CompoundButton.Star" parent="Widget.Material.CompoundButton.Star"/>
+ <style name="Widget.DeviceDefault.TabWidget" parent="Widget.Material.TabWidget"/>
+ <style name="Widget.DeviceDefault.WebTextView" parent="Widget.Material.WebTextView"/>
+ <style name="Widget.DeviceDefault.WebView" parent="Widget.Material.WebView"/>
+ <style name="Widget.DeviceDefault.DropDownItem" parent="Widget.Material.DropDownItem"/>
+ <style name="Widget.DeviceDefault.DropDownItem.Spinner" parent="Widget.Material.DropDownItem.Spinner"/>
+ <style name="Widget.DeviceDefault.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem"/>
+ <style name="Widget.DeviceDefault.ListPopupWindow" parent="Widget.Material.ListPopupWindow"/>
+ <style name="Widget.DeviceDefault.PopupMenu" parent="Widget.Material.PopupMenu"/>
+ <style name="Widget.DeviceDefault.ActionButton" parent="Widget.Material.ActionButton"/>
+ <style name="Widget.DeviceDefault.ActionButton.Overflow" parent="Widget.Material.ActionButton.Overflow"/>
+ <style name="Widget.DeviceDefault.ActionButton.TextButton" parent="Widget.Material.ActionButton"/>
+ <style name="Widget.DeviceDefault.ActionMode" parent="Widget.Material.ActionMode"/>
+ <style name="Widget.DeviceDefault.ActionButton.CloseMode" parent="Widget.Material.ActionButton.CloseMode"/>
+ <style name="Widget.DeviceDefault.ActionBar" parent="Widget.Material.ActionBar"/>
+ <style name="Widget.DeviceDefault.Button.Borderless" parent="Widget.Material.Button.Borderless"/>
+ <style name="Widget.DeviceDefault.Tab" parent="Widget.Material.Tab"/>
+ <style name="Widget.DeviceDefault.CalendarView" parent="Widget.Material.CalendarView"/>
+ <style name="Widget.DeviceDefault.DatePicker" parent="Widget.Material.DatePicker"/>
+ <style name="Widget.DeviceDefault.ActionBar.TabView" parent="Widget.Material.ActionBar.TabView"/>
+ <style name="Widget.DeviceDefault.ActionBar.TabText" parent="Widget.Material.ActionBar.TabText"/>
+ <style name="Widget.DeviceDefault.ActionBar.TabBar" parent="Widget.Material.ActionBar.TabBar"/>
+ <style name="Widget.DeviceDefault.ActionBar.Solid" parent="Widget.Material.ActionBar.Solid"/>
+ <style name="Widget.DeviceDefault.Button.Borderless.Small" parent="Widget.Material.Button.Borderless.Small"/>
+ <style name="Widget.DeviceDefault.AbsListView" parent="Widget.Material.AbsListView"/>
+ <style name="Widget.DeviceDefault.Spinner.DropDown.ActionBar" parent="Widget.Material.Spinner.DropDown.ActionBar"/>
+ <style name="Widget.DeviceDefault.PopupWindow.ActionMode" parent="Widget.Material.PopupWindow.ActionMode"/>
+ <style name="Widget.DeviceDefault.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch"/>
+ <style name="Widget.DeviceDefault.ExpandableListView.White" parent="Widget.Material.ExpandableListView.White"/>
+ <style name="Widget.DeviceDefault.FastScroll" parent="Widget.Material.FastScroll"/>
<!-- The FragmentBreadCrumbs widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). -->
<style name="Widget.DeviceDefault.FragmentBreadCrumbs" parent="Widget.Holo.FragmentBreadCrumbs"/>
- <style name="Widget.DeviceDefault.Gallery" parent="Widget.Quantum.Gallery"/>
- <style name="Widget.DeviceDefault.GestureOverlayView" parent="Widget.Quantum.GestureOverlayView"/>
- <style name="Widget.DeviceDefault.ImageWell" parent="Widget.Quantum.ImageWell"/>
- <style name="Widget.DeviceDefault.KeyboardView" parent="Widget.Quantum.KeyboardView"/>
- <style name="Widget.DeviceDefault.ListView.White" parent="Widget.Quantum.ListView.White"/>
- <style name="Widget.DeviceDefault.MediaRouteButton" parent="Widget.Quantum.MediaRouteButton" />
- <style name="Widget.DeviceDefault.NumberPicker" parent="Widget.Quantum.NumberPicker"/>
- <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Quantum.PreferenceFrameLayout"/>
- <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Quantum.ProgressBar.Inverse"/>
- <style name="Widget.DeviceDefault.ProgressBar.Large.Inverse" parent="Widget.Quantum.ProgressBar.Large.Inverse"/>
- <style name="Widget.DeviceDefault.ProgressBar.Small.Inverse" parent="Widget.Quantum.ProgressBar.Small.Inverse"/>
- <style name="Widget.DeviceDefault.QuickContactBadge.WindowLarge" parent="Widget.Quantum.QuickContactBadge.WindowLarge"/>
- <style name="Widget.DeviceDefault.QuickContactBadge.WindowMedium" parent="Widget.Quantum.QuickContactBadge.WindowMedium"/>
- <style name="Widget.DeviceDefault.QuickContactBadge.WindowSmall" parent="Widget.Quantum.QuickContactBadge.WindowSmall"/>
- <style name="Widget.DeviceDefault.QuickContactBadgeSmall.WindowLarge" parent="Widget.Quantum.QuickContactBadgeSmall.WindowLarge"/>
- <style name="Widget.DeviceDefault.QuickContactBadgeSmall.WindowMedium" parent="Widget.Quantum.QuickContactBadgeSmall.WindowMedium"/>
- <style name="Widget.DeviceDefault.QuickContactBadgeSmall.WindowSmall" parent="Widget.Quantum.QuickContactBadgeSmall.WindowSmall"/>
- <style name="Widget.DeviceDefault.Spinner.DropDown" parent="Widget.Quantum.Spinner.DropDown"/>
- <style name="Widget.DeviceDefault.StackView" parent="Widget.Quantum.StackView"/>
- <style name="Widget.DeviceDefault.TextSelectHandle" parent="Widget.Quantum.TextSelectHandle"/>
- <style name="Widget.DeviceDefault.TextSuggestionsPopupWindow" parent="Widget.Quantum.TextSuggestionsPopupWindow"/>
- <style name="Widget.DeviceDefault.TextView.ListSeparator" parent="Widget.Quantum.TextView.ListSeparator"/>
- <style name="Widget.DeviceDefault.TimePicker" parent="Widget.Quantum.TimePicker"/>
+ <style name="Widget.DeviceDefault.Gallery" parent="Widget.Material.Gallery"/>
+ <style name="Widget.DeviceDefault.GestureOverlayView" parent="Widget.Material.GestureOverlayView"/>
+ <style name="Widget.DeviceDefault.ImageWell" parent="Widget.Material.ImageWell"/>
+ <style name="Widget.DeviceDefault.KeyboardView" parent="Widget.Material.KeyboardView"/>
+ <style name="Widget.DeviceDefault.ListView.White" parent="Widget.Material.ListView.White"/>
+ <style name="Widget.DeviceDefault.MediaRouteButton" parent="Widget.Material.MediaRouteButton" />
+ <style name="Widget.DeviceDefault.NumberPicker" parent="Widget.Material.NumberPicker"/>
+ <style name="Widget.DeviceDefault.PreferenceFrameLayout" parent="Widget.Material.PreferenceFrameLayout"/>
+ <style name="Widget.DeviceDefault.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
+ <style name="Widget.DeviceDefault.ProgressBar.Large.Inverse" parent="Widget.Material.ProgressBar.Large.Inverse"/>
+ <style name="Widget.DeviceDefault.ProgressBar.Small.Inverse" parent="Widget.Material.ProgressBar.Small.Inverse"/>
+ <style name="Widget.DeviceDefault.QuickContactBadge.WindowLarge" parent="Widget.Material.QuickContactBadge.WindowLarge"/>
+ <style name="Widget.DeviceDefault.QuickContactBadge.WindowMedium" parent="Widget.Material.QuickContactBadge.WindowMedium"/>
+ <style name="Widget.DeviceDefault.QuickContactBadge.WindowSmall" parent="Widget.Material.QuickContactBadge.WindowSmall"/>
+ <style name="Widget.DeviceDefault.QuickContactBadgeSmall.WindowLarge" parent="Widget.Material.QuickContactBadgeSmall.WindowLarge"/>
+ <style name="Widget.DeviceDefault.QuickContactBadgeSmall.WindowMedium" parent="Widget.Material.QuickContactBadgeSmall.WindowMedium"/>
+ <style name="Widget.DeviceDefault.QuickContactBadgeSmall.WindowSmall" parent="Widget.Material.QuickContactBadgeSmall.WindowSmall"/>
+ <style name="Widget.DeviceDefault.Spinner.DropDown" parent="Widget.Material.Spinner.DropDown"/>
+ <style name="Widget.DeviceDefault.StackView" parent="Widget.Material.StackView"/>
+ <style name="Widget.DeviceDefault.TextSelectHandle" parent="Widget.Material.TextSelectHandle"/>
+ <style name="Widget.DeviceDefault.TextSuggestionsPopupWindow" parent="Widget.Material.TextSuggestionsPopupWindow"/>
+ <style name="Widget.DeviceDefault.TextView.ListSeparator" parent="Widget.Material.TextView.ListSeparator"/>
+ <style name="Widget.DeviceDefault.TimePicker" parent="Widget.Material.TimePicker"/>
- <style name="Widget.DeviceDefault.Light" parent="Widget.Quantum.Light"/>
- <style name="Widget.DeviceDefault.Light.Button" parent="Widget.Quantum.Light.Button"/>
- <style name="Widget.DeviceDefault.Light.Button.Small" parent="Widget.Quantum.Light.Button.Small"/>
- <style name="Widget.DeviceDefault.Light.Button.Inset" parent="Widget.Quantum.Light.Button.Inset"/>
- <style name="Widget.DeviceDefault.Light.Button.Toggle" parent="Widget.Quantum.Light.Button.Toggle"/>
- <style name="Widget.DeviceDefault.Light.StackView" parent="Widget.Quantum.Light.StackView"/>
- <style name="Widget.DeviceDefault.Light.TextView" parent="Widget.Quantum.Light.TextView"/>
- <style name="Widget.DeviceDefault.Light.CheckedTextView" parent="Widget.Quantum.Light.CheckedTextView"/>
- <style name="Widget.DeviceDefault.Light.AutoCompleteTextView" parent="Widget.Quantum.Light.AutoCompleteTextView"/>
- <style name="Widget.DeviceDefault.Light.CompoundButton.CheckBox" parent="Widget.Quantum.Light.CompoundButton.CheckBox"/>
- <style name="Widget.DeviceDefault.Light.ListView.DropDown" parent="Widget.Quantum.Light.ListView.DropDown"/>
- <style name="Widget.DeviceDefault.Light.EditText" parent="Widget.Quantum.Light.EditText"/>
- <style name="Widget.DeviceDefault.Light.ExpandableListView" parent="Widget.Quantum.Light.ExpandableListView"/>
- <style name="Widget.DeviceDefault.Light.FastScroll" parent="Widget.Quantum.Light.FastScroll"/>
+ <style name="Widget.DeviceDefault.Light" parent="Widget.Material.Light"/>
+ <style name="Widget.DeviceDefault.Light.Button" parent="Widget.Material.Light.Button"/>
+ <style name="Widget.DeviceDefault.Light.Button.Small" parent="Widget.Material.Light.Button.Small"/>
+ <style name="Widget.DeviceDefault.Light.Button.Inset" parent="Widget.Material.Light.Button.Inset"/>
+ <style name="Widget.DeviceDefault.Light.Button.Toggle" parent="Widget.Material.Light.Button.Toggle"/>
+ <style name="Widget.DeviceDefault.Light.StackView" parent="Widget.Material.Light.StackView"/>
+ <style name="Widget.DeviceDefault.Light.TextView" parent="Widget.Material.Light.TextView"/>
+ <style name="Widget.DeviceDefault.Light.CheckedTextView" parent="Widget.Material.Light.CheckedTextView"/>
+ <style name="Widget.DeviceDefault.Light.AutoCompleteTextView" parent="Widget.Material.Light.AutoCompleteTextView"/>
+ <style name="Widget.DeviceDefault.Light.CompoundButton.CheckBox" parent="Widget.Material.Light.CompoundButton.CheckBox"/>
+ <style name="Widget.DeviceDefault.Light.ListView.DropDown" parent="Widget.Material.Light.ListView.DropDown"/>
+ <style name="Widget.DeviceDefault.Light.EditText" parent="Widget.Material.Light.EditText"/>
+ <style name="Widget.DeviceDefault.Light.ExpandableListView" parent="Widget.Material.Light.ExpandableListView"/>
+ <style name="Widget.DeviceDefault.Light.FastScroll" parent="Widget.Material.Light.FastScroll"/>
<!-- The FragmentBreadCrumbs widget is deprecated starting in API level 21 ({@link android.os.Build.VERSION_CODES#.L}). -->
<style name="Widget.DeviceDefault.Light.FragmentBreadCrumbs" parent="Widget.Holo.Light.FragmentBreadCrumbs"/>
- <style name="Widget.DeviceDefault.Light.GridView" parent="Widget.Quantum.Light.GridView"/>
- <style name="Widget.DeviceDefault.Light.ImageButton" parent="Widget.Quantum.Light.ImageButton"/>
- <style name="Widget.DeviceDefault.Light.ListView" parent="Widget.Quantum.Light.ListView"/>
- <style name="Widget.DeviceDefault.Light.MediaRouteButton" parent="Widget.Quantum.Light.MediaRouteButton" />
- <style name="Widget.DeviceDefault.Light.PopupWindow" parent="Widget.Quantum.Light.PopupWindow"/>
- <style name="Widget.DeviceDefault.Light.ProgressBar" parent="Widget.Quantum.Light.ProgressBar"/>
- <style name="Widget.DeviceDefault.Light.ProgressBar.Horizontal" parent="Widget.Quantum.Light.ProgressBar.Horizontal"/>
- <style name="Widget.DeviceDefault.Light.ProgressBar.Small" parent="Widget.Quantum.Light.ProgressBar.Small"/>
- <style name="Widget.DeviceDefault.Light.ProgressBar.Small.Title" parent="Widget.Quantum.Light.ProgressBar.Small.Title"/>
- <style name="Widget.DeviceDefault.Light.ProgressBar.Large" parent="Widget.Quantum.Light.ProgressBar.Large"/>
- <style name="Widget.DeviceDefault.Light.ProgressBar.Inverse" parent="Widget.Quantum.Light.ProgressBar.Inverse"/>
- <style name="Widget.DeviceDefault.Light.ProgressBar.Small.Inverse" parent="Widget.Quantum.Light.ProgressBar.Small.Inverse"/>
- <style name="Widget.DeviceDefault.Light.ProgressBar.Large.Inverse" parent="Widget.Quantum.Light.ProgressBar.Large.Inverse"/>
- <style name="Widget.DeviceDefault.Light.SeekBar" parent="Widget.Quantum.Light.SeekBar"/>
- <style name="Widget.DeviceDefault.Light.RatingBar" parent="Widget.Quantum.Light.RatingBar"/>
- <style name="Widget.DeviceDefault.Light.RatingBar.Indicator" parent="Widget.Quantum.Light.RatingBar.Indicator"/>
- <style name="Widget.DeviceDefault.Light.RatingBar.Small" parent="Widget.Quantum.Light.RatingBar.Small"/>
- <style name="Widget.DeviceDefault.Light.CompoundButton.RadioButton" parent="Widget.Quantum.Light.CompoundButton.RadioButton"/>
- <style name="Widget.DeviceDefault.Light.ScrollView" parent="Widget.Quantum.Light.ScrollView"/>
- <style name="Widget.DeviceDefault.Light.HorizontalScrollView" parent="Widget.Quantum.Light.HorizontalScrollView"/>
- <style name="Widget.DeviceDefault.Light.Spinner" parent="Widget.Quantum.Light.Spinner"/>
- <style name="Widget.DeviceDefault.Light.CompoundButton.Star" parent="Widget.Quantum.Light.CompoundButton.Star"/>
- <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.Quantum.Light.TabWidget"/>
- <style name="Widget.DeviceDefault.Light.WebTextView" parent="Widget.Quantum.Light.WebTextView"/>
- <style name="Widget.DeviceDefault.Light.WebView" parent="Widget.Quantum.Light.WebView"/>
- <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.Quantum.Light.DropDownItem"/>
- <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.Quantum.Light.DropDownItem.Spinner"/>
- <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.Quantum.Light.TextView.SpinnerItem"/>
- <style name="Widget.DeviceDefault.Light.ListPopupWindow" parent="Widget.Quantum.Light.ListPopupWindow"/>
- <style name="Widget.DeviceDefault.Light.PopupMenu" parent="Widget.Quantum.Light.PopupMenu"/>
- <style name="Widget.DeviceDefault.Light.Tab" parent="Widget.Quantum.Light.Tab"/>
- <style name="Widget.DeviceDefault.Light.CalendarView" parent="Widget.Quantum.Light.CalendarView"/>
- <style name="Widget.DeviceDefault.Light.Button.Borderless.Small" parent="Widget.Quantum.Light.Button.Borderless.Small"/>
- <style name="Widget.DeviceDefault.Light.ActionButton" parent="Widget.Quantum.Light.ActionButton"/>
- <style name="Widget.DeviceDefault.Light.ActionButton.Overflow" parent="Widget.Quantum.Light.ActionButton.Overflow"/>
- <style name="Widget.DeviceDefault.Light.ActionMode" parent="Widget.Quantum.Light.ActionMode"/>
- <style name="Widget.DeviceDefault.Light.ActionButton.CloseMode" parent="Widget.Quantum.Light.ActionButton.CloseMode"/>
- <style name="Widget.DeviceDefault.Light.ActionBar" parent="Widget.Quantum.Light.ActionBar"/>
- <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.Quantum.Light.ActionBar.TabView"/>
- <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.Quantum.Light.ActionBar.TabText"/>
- <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.Quantum.Light.ActionBar.TabBar"/>
- <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.Quantum.Light.ActionBar.Solid"/>
+ <style name="Widget.DeviceDefault.Light.GridView" parent="Widget.Material.Light.GridView"/>
+ <style name="Widget.DeviceDefault.Light.ImageButton" parent="Widget.Material.Light.ImageButton"/>
+ <style name="Widget.DeviceDefault.Light.ListView" parent="Widget.Material.Light.ListView"/>
+ <style name="Widget.DeviceDefault.Light.MediaRouteButton" parent="Widget.Material.Light.MediaRouteButton" />
+ <style name="Widget.DeviceDefault.Light.PopupWindow" parent="Widget.Material.Light.PopupWindow"/>
+ <style name="Widget.DeviceDefault.Light.ProgressBar" parent="Widget.Material.Light.ProgressBar"/>
+ <style name="Widget.DeviceDefault.Light.ProgressBar.Horizontal" parent="Widget.Material.Light.ProgressBar.Horizontal"/>
+ <style name="Widget.DeviceDefault.Light.ProgressBar.Small" parent="Widget.Material.Light.ProgressBar.Small"/>
+ <style name="Widget.DeviceDefault.Light.ProgressBar.Small.Title" parent="Widget.Material.Light.ProgressBar.Small.Title"/>
+ <style name="Widget.DeviceDefault.Light.ProgressBar.Large" parent="Widget.Material.Light.ProgressBar.Large"/>
+ <style name="Widget.DeviceDefault.Light.ProgressBar.Inverse" parent="Widget.Material.Light.ProgressBar.Inverse"/>
+ <style name="Widget.DeviceDefault.Light.ProgressBar.Small.Inverse" parent="Widget.Material.Light.ProgressBar.Small.Inverse"/>
+ <style name="Widget.DeviceDefault.Light.ProgressBar.Large.Inverse" parent="Widget.Material.Light.ProgressBar.Large.Inverse"/>
+ <style name="Widget.DeviceDefault.Light.SeekBar" parent="Widget.Material.Light.SeekBar"/>
+ <style name="Widget.DeviceDefault.Light.RatingBar" parent="Widget.Material.Light.RatingBar"/>
+ <style name="Widget.DeviceDefault.Light.RatingBar.Indicator" parent="Widget.Material.Light.RatingBar.Indicator"/>
+ <style name="Widget.DeviceDefault.Light.RatingBar.Small" parent="Widget.Material.Light.RatingBar.Small"/>
+ <style name="Widget.DeviceDefault.Light.CompoundButton.RadioButton" parent="Widget.Material.Light.CompoundButton.RadioButton"/>
+ <style name="Widget.DeviceDefault.Light.ScrollView" parent="Widget.Material.Light.ScrollView"/>
+ <style name="Widget.DeviceDefault.Light.HorizontalScrollView" parent="Widget.Material.Light.HorizontalScrollView"/>
+ <style name="Widget.DeviceDefault.Light.Spinner" parent="Widget.Material.Light.Spinner"/>
+ <style name="Widget.DeviceDefault.Light.CompoundButton.Star" parent="Widget.Material.Light.CompoundButton.Star"/>
+ <style name="Widget.DeviceDefault.Light.TabWidget" parent="Widget.Material.Light.TabWidget"/>
+ <style name="Widget.DeviceDefault.Light.WebTextView" parent="Widget.Material.Light.WebTextView"/>
+ <style name="Widget.DeviceDefault.Light.WebView" parent="Widget.Material.Light.WebView"/>
+ <style name="Widget.DeviceDefault.Light.DropDownItem" parent="Widget.Material.Light.DropDownItem"/>
+ <style name="Widget.DeviceDefault.Light.DropDownItem.Spinner" parent="Widget.Material.Light.DropDownItem.Spinner"/>
+ <style name="Widget.DeviceDefault.Light.TextView.SpinnerItem" parent="Widget.Material.Light.TextView.SpinnerItem"/>
+ <style name="Widget.DeviceDefault.Light.ListPopupWindow" parent="Widget.Material.Light.ListPopupWindow"/>
+ <style name="Widget.DeviceDefault.Light.PopupMenu" parent="Widget.Material.Light.PopupMenu"/>
+ <style name="Widget.DeviceDefault.Light.Tab" parent="Widget.Material.Light.Tab"/>
+ <style name="Widget.DeviceDefault.Light.CalendarView" parent="Widget.Material.Light.CalendarView"/>
+ <style name="Widget.DeviceDefault.Light.Button.Borderless.Small" parent="Widget.Material.Light.Button.Borderless.Small"/>
+ <style name="Widget.DeviceDefault.Light.ActionButton" parent="Widget.Material.Light.ActionButton"/>
+ <style name="Widget.DeviceDefault.Light.ActionButton.Overflow" parent="Widget.Material.Light.ActionButton.Overflow"/>
+ <style name="Widget.DeviceDefault.Light.ActionMode" parent="Widget.Material.Light.ActionMode"/>
+ <style name="Widget.DeviceDefault.Light.ActionButton.CloseMode" parent="Widget.Material.Light.ActionButton.CloseMode"/>
+ <style name="Widget.DeviceDefault.Light.ActionBar" parent="Widget.Material.Light.ActionBar"/>
+ <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.Material.Light.ActionBar.TabView"/>
+ <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.Material.Light.ActionBar.TabText"/>
+ <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.Material.Light.ActionBar.TabBar"/>
+ <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.Material.Light.ActionBar.Solid"/>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
<style name="Widget.DeviceDefault.Light.ActionBar.Solid.Inverse" parent="Widget.Holo.Light.ActionBar.Solid.Inverse"/>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
@@ -185,98 +185,98 @@
<style name="Widget.DeviceDefault.Light.ActionBar.TabText.Inverse" parent="Widget.Holo.Light.ActionBar.TabText.Inverse"/>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
<style name="Widget.DeviceDefault.Light.ActionMode.Inverse" parent="Widget.Holo.Light.ActionMode.Inverse"/>
- <style name="Widget.DeviceDefault.Light.AbsListView" parent="Widget.Quantum.Light.AbsListView"/>
- <style name="Widget.DeviceDefault.Light.Spinner.DropDown.ActionBar" parent="Widget.Quantum.Light.Spinner.DropDown.ActionBar"/>
- <style name="Widget.DeviceDefault.Light.PopupWindow.ActionMode" parent="Widget.Quantum.Light.PopupWindow.ActionMode"/>
- <style name="Widget.DeviceDefault.Light.Button.Borderless" parent="Widget.Quantum.Light.Button.Borderless"/>
- <style name="Widget.DeviceDefault.Light.DatePicker" parent="Widget.Quantum.Light.DatePicker"/>
- <style name="Widget.DeviceDefault.Light.ExpandableListView.White" parent="Widget.Quantum.Light.ExpandableListView.White"/>
- <style name="Widget.DeviceDefault.Light.Gallery" parent="Widget.Quantum.Light.Gallery"/>
- <style name="Widget.DeviceDefault.Light.GestureOverlayView" parent="Widget.Quantum.Light.GestureOverlayView"/>
- <style name="Widget.DeviceDefault.Light.ImageWell" parent="Widget.Quantum.Light.ImageWell"/>
- <style name="Widget.DeviceDefault.Light.ListView.White" parent="Widget.Quantum.Light.ListView.White"/>
- <style name="Widget.DeviceDefault.Light.NumberPicker" parent="Widget.Quantum.Light.NumberPicker"/>
- <style name="Widget.DeviceDefault.Light.Spinner.DropDown" parent="Widget.Quantum.Light.Spinner.DropDown"/>
- <style name="Widget.DeviceDefault.Light.TextView.ListSeparator" parent="Widget.Quantum.Light.TextView.ListSeparator"/>
- <style name="Widget.DeviceDefault.Light.TimePicker" parent="Widget.Quantum.Light.TimePicker"/>
- <style name="Widget.DeviceDefault.Light.TextSuggestionsPopupWindow" parent="Widget.Quantum.Light.TextSuggestionsPopupWindow"/>
+ <style name="Widget.DeviceDefault.Light.AbsListView" parent="Widget.Material.Light.AbsListView"/>
+ <style name="Widget.DeviceDefault.Light.Spinner.DropDown.ActionBar" parent="Widget.Material.Light.Spinner.DropDown.ActionBar"/>
+ <style name="Widget.DeviceDefault.Light.PopupWindow.ActionMode" parent="Widget.Material.Light.PopupWindow.ActionMode"/>
+ <style name="Widget.DeviceDefault.Light.Button.Borderless" parent="Widget.Material.Light.Button.Borderless"/>
+ <style name="Widget.DeviceDefault.Light.DatePicker" parent="Widget.Material.Light.DatePicker"/>
+ <style name="Widget.DeviceDefault.Light.ExpandableListView.White" parent="Widget.Material.Light.ExpandableListView.White"/>
+ <style name="Widget.DeviceDefault.Light.Gallery" parent="Widget.Material.Light.Gallery"/>
+ <style name="Widget.DeviceDefault.Light.GestureOverlayView" parent="Widget.Material.Light.GestureOverlayView"/>
+ <style name="Widget.DeviceDefault.Light.ImageWell" parent="Widget.Material.Light.ImageWell"/>
+ <style name="Widget.DeviceDefault.Light.ListView.White" parent="Widget.Material.Light.ListView.White"/>
+ <style name="Widget.DeviceDefault.Light.NumberPicker" parent="Widget.Material.Light.NumberPicker"/>
+ <style name="Widget.DeviceDefault.Light.Spinner.DropDown" parent="Widget.Material.Light.Spinner.DropDown"/>
+ <style name="Widget.DeviceDefault.Light.TextView.ListSeparator" parent="Widget.Material.Light.TextView.ListSeparator"/>
+ <style name="Widget.DeviceDefault.Light.TimePicker" parent="Widget.Material.Light.TimePicker"/>
+ <style name="Widget.DeviceDefault.Light.TextSuggestionsPopupWindow" parent="Widget.Material.Light.TextSuggestionsPopupWindow"/>
<!-- Text Appearance Styles -->
- <style name="TextAppearance.DeviceDefault" parent="TextAppearance.Quantum"/>
- <style name="TextAppearance.DeviceDefault.Inverse" parent="TextAppearance.Quantum.Inverse"/>
- <style name="TextAppearance.DeviceDefault.Large" parent="TextAppearance.Quantum.Large"/>
- <style name="TextAppearance.DeviceDefault.Large.Inverse" parent="TextAppearance.Quantum.Large.Inverse"/>
- <style name="TextAppearance.DeviceDefault.Medium" parent="TextAppearance.Quantum.Medium"/>
- <style name="TextAppearance.DeviceDefault.Medium.Inverse" parent="TextAppearance.Quantum.Medium.Inverse"/>
- <style name="TextAppearance.DeviceDefault.Small" parent="TextAppearance.Quantum.Small"/>
- <style name="TextAppearance.DeviceDefault.Small.Inverse" parent="TextAppearance.Quantum.Small.Inverse"/>
- <style name="TextAppearance.DeviceDefault.SearchResult.Title" parent="TextAppearance.Quantum.SearchResult.Title"/>
- <style name="TextAppearance.DeviceDefault.SearchResult.Subtitle" parent="TextAppearance.Quantum.SearchResult.Subtitle"/>
- <style name="TextAppearance.DeviceDefault.TimePicker.TimeLabel" parent="TextAppearance.Quantum.TimePicker.TimeLabel"/>
- <style name="TextAppearance.DeviceDefault.TimePicker.AmPmLabel" parent="TextAppearance.Quantum.TimePicker.AmPmLabel"/>
- <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Quantum.Widget"/>
- <style name="TextAppearance.DeviceDefault.Widget.Button" parent="TextAppearance.Quantum.Widget.Button"/>
- <style name="TextAppearance.DeviceDefault.Widget.IconMenu.Item" parent="TextAppearance.Quantum.Widget.IconMenu.Item"/>
- <style name="TextAppearance.DeviceDefault.Widget.TabWidget" parent="TextAppearance.Quantum.Widget.TabWidget"/>
- <style name="TextAppearance.DeviceDefault.Widget.TextView" parent="TextAppearance.Quantum.Widget.TextView"/>
- <style name="TextAppearance.DeviceDefault.Widget.TextView.PopupMenu" parent="TextAppearance.Quantum.Widget.TextView.PopupMenu"/>
- <style name="TextAppearance.DeviceDefault.Widget.DropDownHint" parent="TextAppearance.Quantum.Widget.DropDownHint"/>
- <style name="TextAppearance.DeviceDefault.Widget.DropDownItem" parent="TextAppearance.Quantum.Widget.DropDownItem"/>
- <style name="TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem" parent="TextAppearance.Quantum.Widget.TextView.SpinnerItem"/>
- <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Quantum.Widget.EditText"/>
- <style name="TextAppearance.DeviceDefault.Widget.PopupMenu" parent="TextAppearance.Quantum.Widget.PopupMenu"/>
- <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Large" parent="TextAppearance.Quantum.Widget.PopupMenu.Large"/>
- <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Small" parent="TextAppearance.Quantum.Widget.PopupMenu.Small"/>
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title" parent="TextAppearance.Quantum.Widget.ActionBar.Title"/>
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Quantum.Widget.ActionBar.Subtitle"/>
- <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Quantum.Widget.ActionMode.Title"/>
- <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Quantum.Widget.ActionMode.Subtitle"/>
- <style name="TextAppearance.DeviceDefault.WindowTitle" parent="TextAppearance.Quantum.WindowTitle"/>
- <style name="TextAppearance.DeviceDefault.DialogWindowTitle" parent="TextAppearance.Quantum.DialogWindowTitle"/>
+ <style name="TextAppearance.DeviceDefault" parent="TextAppearance.Material"/>
+ <style name="TextAppearance.DeviceDefault.Inverse" parent="TextAppearance.Material.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Large" parent="TextAppearance.Material.Large"/>
+ <style name="TextAppearance.DeviceDefault.Large.Inverse" parent="TextAppearance.Material.Large.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Medium" parent="TextAppearance.Material.Medium"/>
+ <style name="TextAppearance.DeviceDefault.Medium.Inverse" parent="TextAppearance.Material.Medium.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Small" parent="TextAppearance.Material.Small"/>
+ <style name="TextAppearance.DeviceDefault.Small.Inverse" parent="TextAppearance.Material.Small.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.SearchResult.Title" parent="TextAppearance.Material.SearchResult.Title"/>
+ <style name="TextAppearance.DeviceDefault.SearchResult.Subtitle" parent="TextAppearance.Material.SearchResult.Subtitle"/>
+ <style name="TextAppearance.DeviceDefault.TimePicker.TimeLabel" parent="TextAppearance.Material.TimePicker.TimeLabel"/>
+ <style name="TextAppearance.DeviceDefault.TimePicker.AmPmLabel" parent="TextAppearance.Material.TimePicker.AmPmLabel"/>
+ <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget"/>
+ <style name="TextAppearance.DeviceDefault.Widget.Button" parent="TextAppearance.Material.Widget.Button"/>
+ <style name="TextAppearance.DeviceDefault.Widget.IconMenu.Item" parent="TextAppearance.Material.Widget.IconMenu.Item"/>
+ <style name="TextAppearance.DeviceDefault.Widget.TabWidget" parent="TextAppearance.Material.Widget.TabWidget"/>
+ <style name="TextAppearance.DeviceDefault.Widget.TextView" parent="TextAppearance.Material.Widget.TextView"/>
+ <style name="TextAppearance.DeviceDefault.Widget.TextView.PopupMenu" parent="TextAppearance.Material.Widget.TextView.PopupMenu"/>
+ <style name="TextAppearance.DeviceDefault.Widget.DropDownHint" parent="TextAppearance.Material.Widget.DropDownHint"/>
+ <style name="TextAppearance.DeviceDefault.Widget.DropDownItem" parent="TextAppearance.Material.Widget.DropDownItem"/>
+ <style name="TextAppearance.DeviceDefault.Widget.TextView.SpinnerItem" parent="TextAppearance.Material.Widget.TextView.SpinnerItem"/>
+ <style name="TextAppearance.DeviceDefault.Widget.EditText" parent="TextAppearance.Material.Widget.EditText"/>
+ <style name="TextAppearance.DeviceDefault.Widget.PopupMenu" parent="TextAppearance.Material.Widget.PopupMenu"/>
+ <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Large" parent="TextAppearance.Material.Widget.PopupMenu.Large"/>
+ <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Small" parent="TextAppearance.Material.Widget.PopupMenu.Small"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title" parent="TextAppearance.Material.Widget.ActionBar.Title"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Widget.ActionBar.Subtitle"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Material.Widget.ActionMode.Title"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Widget.ActionMode.Subtitle"/>
+ <style name="TextAppearance.DeviceDefault.WindowTitle" parent="TextAppearance.Material.WindowTitle"/>
+ <style name="TextAppearance.DeviceDefault.DialogWindowTitle" parent="TextAppearance.Material.DialogWindowTitle"/>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Quantum.Widget.ActionBar.Title.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Title.Inverse"/>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle.Inverse" parent="TextAppearance.Quantum.Widget.ActionBar.Subtitle.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Subtitle.Inverse"/>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
- <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title.Inverse" parent="TextAppearance.Quantum.Widget.ActionMode.Title.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Title.Inverse"/>
<!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
- <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Quantum.Widget.ActionMode.Subtitle.Inverse"/>
- <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Quantum.Widget.ActionBar.Menu"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse"/>
+ <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu"/>
<!-- Preference Styles -->
- <style name="Preference.DeviceDefault" parent="Preference.Quantum"/>
- <style name="Preference.DeviceDefault.Category" parent="Preference.Quantum.Category"/>
- <style name="Preference.DeviceDefault.CheckBoxPreference" parent="Preference.Quantum.CheckBoxPreference"/>
- <style name="Preference.DeviceDefault.DialogPreference" parent="Preference.Quantum.DialogPreference"/>
- <style name="Preference.DeviceDefault.DialogPreference.EditTextPreference" parent="Preference.Quantum.DialogPreference.EditTextPreference"/>
- <style name="Preference.DeviceDefault.DialogPreference.YesNoPreference" parent="Preference.Quantum.DialogPreference.YesNoPreference"/>
- <style name="Preference.DeviceDefault.Information" parent="Preference.Quantum.Information"/>
- <style name="Preference.DeviceDefault.PreferenceScreen" parent="Preference.Quantum.PreferenceScreen"/>
- <style name="Preference.DeviceDefault.RingtonePreference" parent="Preference.Quantum.RingtonePreference"/>
- <style name="Preference.DeviceDefault.SwitchPreference" parent="Preference.Quantum.SwitchPreference"/>
+ <style name="Preference.DeviceDefault" parent="Preference.Material"/>
+ <style name="Preference.DeviceDefault.Category" parent="Preference.Material.Category"/>
+ <style name="Preference.DeviceDefault.CheckBoxPreference" parent="Preference.Material.CheckBoxPreference"/>
+ <style name="Preference.DeviceDefault.DialogPreference" parent="Preference.Material.DialogPreference"/>
+ <style name="Preference.DeviceDefault.DialogPreference.EditTextPreference" parent="Preference.Material.DialogPreference.EditTextPreference"/>
+ <style name="Preference.DeviceDefault.DialogPreference.YesNoPreference" parent="Preference.Material.DialogPreference.YesNoPreference"/>
+ <style name="Preference.DeviceDefault.Information" parent="Preference.Material.Information"/>
+ <style name="Preference.DeviceDefault.PreferenceScreen" parent="Preference.Material.PreferenceScreen"/>
+ <style name="Preference.DeviceDefault.RingtonePreference" parent="Preference.Material.RingtonePreference"/>
+ <style name="Preference.DeviceDefault.SwitchPreference" parent="Preference.Material.SwitchPreference"/>
<!-- AlertDialog Styles -->
- <style name="AlertDialog.DeviceDefault" parent="AlertDialog.Quantum"/>
- <style name="AlertDialog.DeviceDefault.Light" parent="AlertDialog.Quantum.Light"/>
+ <style name="AlertDialog.DeviceDefault" parent="AlertDialog.Material"/>
+ <style name="AlertDialog.DeviceDefault.Light" parent="AlertDialog.Material.Light"/>
<!-- Animation Styles -->
- <style name="Animation.DeviceDefault.Activity" parent="Animation.Quantum.Activity"/>
- <style name="Animation.DeviceDefault.Dialog" parent="Animation.Quantum.Dialog"/>
+ <style name="Animation.DeviceDefault.Activity" parent="Animation.Material.Activity"/>
+ <style name="Animation.DeviceDefault.Dialog" parent="Animation.Material.Dialog"/>
<!-- DialogWindowTitle Styles -->
- <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Quantum"/>
- <style name="DialogWindowTitle.DeviceDefault.Light" parent="DialogWindowTitle.Quantum.Light"/>
+ <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material"/>
+ <style name="DialogWindowTitle.DeviceDefault.Light" parent="DialogWindowTitle.Material.Light"/>
<!-- WindowTitle Styles -->
- <style name="WindowTitle.DeviceDefault" parent="WindowTitle.Quantum"/>
- <style name="WindowTitleBackground.DeviceDefault" parent="WindowTitleBackground.Quantum"/>
+ <style name="WindowTitle.DeviceDefault" parent="WindowTitle.Material"/>
+ <style name="WindowTitleBackground.DeviceDefault" parent="WindowTitleBackground.Material"/>
<!-- Other Styles -->
- <style name="DeviceDefault.ButtonBar" parent="Widget.Quantum.ButtonBar"/>
- <style name="DeviceDefault.ButtonBar.AlertDialog" parent="Widget.Quantum.ButtonBar.AlertDialog"/>
- <style name="DeviceDefault.SegmentedButton" parent="Widget.Quantum.SegmentedButton"/>
+ <style name="DeviceDefault.ButtonBar" parent="Widget.Material.ButtonBar"/>
+ <style name="DeviceDefault.ButtonBar.AlertDialog" parent="Widget.Material.ButtonBar.AlertDialog"/>
+ <style name="DeviceDefault.SegmentedButton" parent="Widget.Material.SegmentedButton"/>
- <style name="DeviceDefault.Light.ButtonBar" parent="Widget.Quantum.Light.ButtonBar"/>
- <style name="DeviceDefault.Light.ButtonBar.AlertDialog" parent="Widget.Quantum.Light.ButtonBar.AlertDialog"/>
- <style name="DeviceDefault.Light.SegmentedButton" parent="Widget.Quantum.Light.SegmentedButton"/>
+ <style name="DeviceDefault.Light.ButtonBar" parent="Widget.Material.Light.ButtonBar"/>
+ <style name="DeviceDefault.Light.ButtonBar.AlertDialog" parent="Widget.Material.Light.ButtonBar.AlertDialog"/>
+ <style name="DeviceDefault.Light.SegmentedButton" parent="Widget.Material.Light.SegmentedButton"/>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
new file mode 100644
index 0000000..ddd82c3
--- /dev/null
+++ b/core/res/res/values/styles_material.xml
@@ -0,0 +1,987 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see styles_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+<resources>
+ <!-- Preference styles -->
+ <eat-comment/>
+
+ <style name="Preference.Material">
+ <item name="layout">@layout/preference_material</item>
+ </style>
+
+ <style name="PreferenceFragment.Material">
+ <item name="paddingStart">@dimen/preference_fragment_padding_side</item>
+ <item name="paddingEnd">@dimen/preference_fragment_padding_side</item>
+ </style>
+
+ <style name="Preference.Material.Information">
+ <item name="layout">@layout/preference_information_material</item>
+ <item name="enabled">false</item>
+ <item name="shouldDisableView">false</item>
+ </style>
+
+ <style name="Preference.Material.Category">
+ <item name="layout">@layout/preference_category_material</item>
+ <!-- The title should not dim if the category is disabled, instead only the preference children should dim. -->
+ <item name="shouldDisableView">false</item>
+ <item name="selectable">false</item>
+ </style>
+
+ <style name="Preference.Material.CheckBoxPreference">
+ <item name="widgetLayout">@layout/preference_widget_checkbox</item>
+ </style>
+
+ <style name="Preference.Material.SwitchPreference">
+ <item name="widgetLayout">@layout/preference_widget_switch</item>
+ <item name="switchTextOn">@string/capital_on</item>
+ <item name="switchTextOff">@string/capital_off</item>
+ </style>
+
+ <style name="Preference.Material.PreferenceScreen"/>
+
+ <style name="Preference.Material.DialogPreference">
+ <item name="positiveButtonText">@string/ok</item>
+ <item name="negativeButtonText">@string/cancel</item>
+ </style>
+
+ <style name="Preference.Material.DialogPreference.YesNoPreference">
+ <item name="positiveButtonText">@string/yes</item>
+ <item name="negativeButtonText">@string/no</item>
+ </style>
+
+ <style name="Preference.Material.DialogPreference.EditTextPreference">
+ <item name="dialogLayout">@layout/preference_dialog_edittext</item>
+ </style>
+
+ <style name="Preference.Material.RingtonePreference">
+ <item name="ringtoneType">ringtone</item>
+ <item name="showSilent">true</item>
+ <item name="showDefault">true</item>
+ </style>
+
+ <!-- Begin Material theme styles -->
+
+ <!-- Text styles -->
+
+ <style name="TextAppearance.Material">
+ <item name="textColor">?attr/textColorPrimary</item>
+ <item name="textColorHint">?attr/textColorHint</item>
+ <item name="textColorHighlight">?attr/textColorHighlight</item>
+ <item name="textColorLink">?attr/textColorLink</item>
+ <item name="textSize">@dimen/text_size_body_1_material</item>
+ <item name="fontFamily">@string/font_family_body_1_material</item>
+ </style>
+
+ <style name="TextAppearance.Material.Display4">
+ <item name="textSize">@dimen/text_size_display_4_material</item>
+ <item name="fontFamily">@string/font_family_display_4_material</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Display3">
+ <item name="textSize">@dimen/text_size_display_3_material</item>
+ <item name="fontFamily">@string/font_family_display_3_material</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Display2">
+ <item name="textSize">@dimen/text_size_display_2_material</item>
+ <item name="fontFamily">@string/font_family_display_2_material</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Display1">
+ <item name="textSize">@dimen/text_size_display_1_material</item>
+ <item name="fontFamily">@string/font_family_display_1_material</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Headline">
+ <item name="textSize">@dimen/text_size_headline_material</item>
+ <item name="fontFamily">@string/font_family_headline_material</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Title">
+ <item name="textSize">@dimen/text_size_title_material</item>
+ <item name="fontFamily">@string/font_family_title_material</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Title.Inverse">
+ <item name="textColor">?attr/textColorPrimaryInverse</item>
+ <item name="textColorHint">?attr/textColorHintInverse</item>
+ <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
+ <item name="textColorLink">?attr/textColorLinkInverse</item>
+ </style>
+
+ <style name="TextAppearance.Material.Subhead">
+ <item name="textSize">@dimen/text_size_subhead_material</item>
+ <item name="fontFamily">@string/font_family_subhead_material</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Subhead.Inverse">
+ <item name="textColor">?attr/textColorSecondaryInverse</item>
+ <item name="textColorHint">?attr/textColorHintInverse</item>
+ <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
+ <item name="textColorLink">?attr/textColorLinkInverse</item>
+ </style>
+
+ <style name="TextAppearance.Material.Body2">
+ <item name="textSize">@dimen/text_size_body_2_material</item>
+ <item name="fontFamily">@string/font_family_body_2_material</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Body1">
+ <item name="textSize">@dimen/text_size_body_1_material</item>
+ <item name="fontFamily">@string/font_family_body_1_material</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Caption">
+ <item name="textSize">@dimen/text_size_caption_material</item>
+ <item name="fontFamily">@string/font_family_caption_material</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Menu">
+ <item name="textSize">@dimen/text_size_menu_material</item>
+ <item name="fontFamily">@string/font_family_menu_material</item>
+ </style>
+
+ <style name="TextAppearance.Material.Menu.Inverse">
+ <item name="textColor">?attr/textColorSecondaryInverse</item>
+ <item name="textColorHint">?attr/textColorHintInverse</item>
+ <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
+ <item name="textColorLink">?attr/textColorLinkInverse</item>
+ </style>
+
+ <style name="TextAppearance.Material.Button">
+ <item name="textSize">@dimen/text_size_button_material</item>
+ <item name="fontFamily">@string/font_family_button_material</item>
+ <item name="textAllCaps">true</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <!-- Deprecated text styles -->
+
+ <style name="TextAppearance.Material.Inverse">
+ <item name="textColor">?attr/textColorPrimaryInverse</item>
+ <item name="textColorHint">?attr/textColorHintInverse</item>
+ <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
+ <item name="textColorLink">?attr/textColorLinkInverse</item>
+ </style>
+
+ <style name="TextAppearance.Material.Large">
+ <item name="textSize">@dimen/text_size_large_material</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Large.Inverse">
+ <item name="textColor">?attr/textColorPrimaryInverse</item>
+ <item name="textColorHint">?attr/textColorHintInverse</item>
+ <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
+ <item name="textColorLink">?attr/textColorLinkInverse</item>
+ </style>
+
+ <style name="TextAppearance.Material.Medium">
+ <item name="textSize">@dimen/text_size_medium_material</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Medium.Inverse">
+ <item name="textColor">?attr/textColorSecondaryInverse</item>
+ <item name="textColorHint">?attr/textColorHintInverse</item>
+ <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
+ <item name="textColorLink">?attr/textColorLinkInverse</item>
+ </style>
+
+ <style name="TextAppearance.Material.Small">
+ <item name="textSize">@dimen/text_size_small_material</item>
+ <item name="textColor">?attr/textColorTertiary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Small.Inverse">
+ <item name="textColor">?attr/textColorTertiaryInverse</item>
+ <item name="textColorHint">?attr/textColorHintInverse</item>
+ <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
+ <item name="textColorLink">?attr/textColorLinkInverse</item>
+ </style>
+
+ <style name="TextAppearance.Material.SearchResult" />
+ <style name="TextAppearance.Material.SearchResult.Title" parent="TextAppearance.Material.Title" />
+ <style name="TextAppearance.Material.SearchResult.Subtitle" parent="TextAppearance.Material.Subhead" />
+
+ <style name="TextAppearance.Material.Widget"/>
+ <style name="TextAppearance.Material.Widget.Button" parent="TextAppearance.Material.Button" />
+
+ <style name="TextAppearance.Material.Widget.EditText">
+ <item name="textColor">?attr/textColorPrimaryInverse</item>
+ <item name="textColorHint">?attr/textColorHintInverse</item>
+ </style>
+
+ <style name="TextAppearance.Material.Widget.Switch" parent="TextAppearance.Material.Button" />
+
+ <style name="TextAppearance.Material.Widget.PopupMenu"/>
+ <style name="TextAppearance.Material.Widget.PopupMenu.Large" parent="TextAppearance.Material.Menu" />
+ <style name="TextAppearance.Material.Widget.PopupMenu.Small" parent="TextAppearance.Material.Menu" />
+
+ <style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
+
+ <style name="TextAppearance.Material.Widget.IconMenu.Item" parent="TextAppearance.Material.Small">
+ <item name="textColor">?attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.Material.Widget.TabWidget" parent="TextAppearance.Material.Button" />
+
+ <style name="TextAppearance.Material.Widget.TextView">
+ <item name="textColor">?attr/textColorPrimaryDisableOnly</item>
+ <item name="textColorHint">?attr/textColorHint</item>
+ </style>
+
+ <style name="TextAppearance.Material.Widget.TextView.PopupMenu" parent="TextAppearance.Material.Menu" />
+ <style name="TextAppearance.Material.Widget.TextView.SpinnerItem" />
+
+ <style name="TextAppearance.Material.Widget.DropDownItem">
+ <item name="textColor">?attr/textColorPrimaryDisableOnly</item>
+ </style>
+
+ <style name="TextAppearance.Material.Widget.ActionMode"/>
+ <style name="TextAppearance.Material.Widget.ActionMode.Title" parent="TextAppearance.Material.Title" />
+ <style name="TextAppearance.Material.Widget.ActionMode.Title.Inverse" parent="TextAppearance.Material.Title.Inverse" />
+ <style name="TextAppearance.Material.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Subhead" />
+ <style name="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Subhead.Inverse" />
+
+ <style name="TextAppearance.Material.Widget.ActionBar.Title" parent="TextAppearance.Material.Title" />
+ <style name="TextAppearance.Material.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Material.Title.Inverse" />
+ <style name="TextAppearance.Material.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Subhead" />
+ <style name="TextAppearance.Material.Widget.ActionBar.Subtitle.Inverse" parent="TextAppearance.Material.Subhead.Inverse" />
+
+ <style name="TextAppearance.Material.Widget.ActionBar.Menu" parent="TextAppearance.Material.Menu">
+ <item name="textColor">?attr/actionMenuTextColor</item>
+ <item name="textAllCaps">@bool/config_actionMenuItemAllCaps</item>
+ </style>
+
+ <style name="TextAppearance.Material.Widget.ActionBar.Menu.Inverse" parent="TextAppearance.Material.Menu.Inverse">
+ <item name="textColor">?attr/actionMenuTextColor</item>
+ <item name="textAllCaps">@bool/config_actionMenuItemAllCaps</item>
+ </style>
+
+ <style name="TextAppearance.Material.WindowTitle" parent="TextAppearance.Material.Title" />
+ <style name="TextAppearance.Material.DialogWindowTitle" parent="TextAppearance.Material.Title" />
+
+ <style name="TextAppearance.Material.CalendarViewWeekDayView" parent="TextAppearance.Material.Small">
+ <item name="textStyle">bold</item>
+ <item name="textColor">#505050</item>
+ </style>
+
+ <style name="TextAppearance.Material.TimePicker.TimeLabel" parent="TextAppearance.Material">
+ <item name="textSize">@dimen/timepicker_time_label_size</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ </style>
+
+ <style name="TextAppearance.Material.TimePicker.AmPmLabel" parent="TextAppearance.Material">
+ <item name="textSize">@dimen/timepicker_ampm_label_size</item>
+ <item name="textAllCaps">true</item>
+ <item name="textColor">?attr/textColorSecondary</item>
+ <item name="textStyle">bold</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Material" />
+
+ <style name="TextAppearance.StatusBar.Material.EventContent">
+ <item name="android:textColor">#90000000</item>
+ <item name="android:textSize">@dimen/notification_text_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Material.EventContent.Title">
+ <item name="android:textColor">#DD000000</item>
+ <item name="android:textSize">@dimen/notification_title_text_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Material.EventContent.Line2">
+ <item name="android:textSize">@dimen/notification_subtext_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Material.EventContent.Info">
+ <item name="android:textSize">@dimen/notification_subtext_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Material.EventContent.Time">
+ <item name="android:textSize">@dimen/notification_subtext_size</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Material.EventContent.Emphasis">
+ <item name="android:textColor">#66000000</item>
+ </style>
+
+ <style name="Widget.StatusBar.Material.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
+
+ <style name="Widget.StatusBar.Material.ProgressBar.Media">
+ <item name="android:progressDrawable">@drawable/notification_material_media_progress</item>
+ </style>
+
+ <!-- Widget Styles -->
+
+ <style name="Material"/>
+ <style name="Material.Light"/>
+ <style name="Widget.Material" parent="Widget" />
+
+ <!-- Bordered ink button -->
+ <style name="Widget.Material.Button" parent="Widget.Button">
+ <item name="background">@drawable/btn_default_material</item>
+ <item name="textAppearance">?attr/textAppearanceButton</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ <item name="minHeight">48dip</item>
+ <item name="minWidth">88dip</item>
+
+ <!-- TODO: Turn this back on when we support inset drawable outlines. -->
+ <!-- <item name="stateListAnimator">@anim/button_state_list_anim_material</item> -->
+ </style>
+
+ <!-- Small bordered ink button -->
+ <style name="Widget.Material.Button.Small">
+ <item name="minHeight">48dip</item>
+ <item name="minWidth">48dip</item>
+ </style>
+
+ <!-- Borderless ink button -->
+ <style name="Widget.Material.Button.Borderless">
+ <item name="background">@drawable/btn_borderless_material</item>
+ <item name="stateListAnimator">@null</item>
+ </style>
+
+ <!-- Small borderless ink button -->
+ <style name="Widget.Material.Button.Borderless.Small">
+ <item name="minHeight">48dip</item>
+ <item name="minWidth">48dip</item>
+ </style>
+
+ <style name="Widget.Material.Button.Inset">
+ <item name="background">@drawable/button_inset</item>
+ </style>
+
+ <style name="Widget.Material.Button.Toggle">
+ <item name="background">@drawable/btn_toggle_material</item>
+ <item name="textOn">@string/capital_on</item>
+ <item name="textOff">@string/capital_off</item>
+ <item name="minHeight">48dip</item>
+ </style>
+
+ <style name="Widget.Material.ButtonBar">
+ <item name="background">@null</item>
+ </style>
+
+ <style name="Widget.Material.ButtonBar.AlertDialog">
+ <item name="background">@null</item>
+ </style>
+
+ <style name="Widget.Material.SegmentedButton" parent="SegmentedButton">
+ <item name="background">@drawable/btn_group_holo_dark</item>
+ </style>
+
+ <style name="Widget.Material.StackView">
+ <item name="resOutColor">@color/holo_blue_light</item>
+ <item name="clickColor">@color/holo_blue_light</item>
+ </style>
+
+ <style name="Widget.Material.TextView" parent="Widget.TextView"/>
+
+ <style name="Widget.Material.TextView.ListSeparator" parent="Widget.TextView.ListSeparator">
+ <item name="background">@drawable/list_section_divider_material</item>
+ <item name="textAllCaps">true</item>
+ </style>
+
+ <style name="Widget.Material.TextView.SpinnerItem" parent="Widget.TextView.SpinnerItem">
+ <item name="textAppearance">@style/TextAppearance.Material.Widget.TextView.SpinnerItem</item>
+ <item name="paddingStart">8dp</item>
+ <item name="paddingEnd">8dp</item>
+ </style>
+
+ <style name="Widget.Material.CheckedTextView" parent="Widget.CheckedTextView" />
+ <style name="Widget.Material.TextSelectHandle" parent="Widget.TextSelectHandle"/>
+ <style name="Widget.Material.TextSuggestionsPopupWindow" parent="Widget.TextSuggestionsPopupWindow"/>
+ <style name="Widget.Material.AbsListView" parent="Widget.AbsListView"/>
+
+ <style name="Widget.Material.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
+ <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
+ <item name="popupBackground">@drawable/popup_background_material</item>
+ </style>
+
+ <style name="Widget.Material.CompoundButton" parent="Widget.CompoundButton"/>
+
+ <style name="Widget.Material.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
+ </style>
+
+ <style name="Widget.Material.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
+ </style>
+
+ <style name="Widget.Material.CompoundButton.Star" parent="Widget.CompoundButton.Star">
+ <item name="button">@drawable/btn_star_material</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
+ </style>
+
+ <style name="Widget.Material.CompoundButton.Switch">
+ <item name="track">@drawable/switch_track_material</item>
+ <item name="thumb">@drawable/switch_thumb_material_anim</item>
+ <item name="splitTrack">true</item>
+ <item name="switchTextAppearance">@style/TextAppearance.Material.Widget.Switch</item>
+ <item name="textOn"></item>
+ <item name="textOff"></item>
+ <item name="switchMinWidth">4dip</item>
+ <item name="switchPadding">4dip</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
+ </style>
+
+ <style name="Widget.Material.EditText" parent="Widget.EditText"/>
+
+ <style name="Widget.Material.ExpandableListView" parent="Widget.Material.ListView">
+ <item name="groupIndicator">@drawable/expander_group_material</item>
+ <item name="indicatorLeft">?attr/expandableListPreferredItemIndicatorLeft</item>
+ <item name="indicatorRight">?attr/expandableListPreferredItemIndicatorRight</item>
+ <item name="childDivider">?attr/listDivider</item>
+ </style>
+
+ <style name="Widget.Material.ExpandableListView.White"/>
+ <style name="Widget.Material.Gallery" parent="Widget.Gallery"/>
+ <style name="Widget.Material.GestureOverlayView" parent="Widget.GestureOverlayView"/>
+
+ <style name="Widget.Material.GridView" parent="Widget.GridView">
+ <item name="android:listSelector">?attr/selectableItemBackground</item>
+ </style>
+
+ <style name="Widget.Material.CalendarView" parent="Widget.CalendarView">
+ <item name="selectedWeekBackgroundColor">#330099FF</item>
+ <item name="focusedMonthDateColor">#FFFFFFFF</item>
+ <item name="unfocusedMonthDateColor">#66FFFFFF</item>
+ <item name="weekNumberColor">#33FFFFFF</item>
+ <item name="weekSeparatorLineColor">#19FFFFFF</item>
+ <item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
+ <item name="weekDayTextAppearance">@style/TextAppearance.Material.CalendarViewWeekDayView</item>
+ </style>
+
+ <style name="Widget.Material.ImageButton" parent="Widget.ImageButton">
+ <item name="background">@drawable/btn_default_material</item>
+ </style>
+
+ <style name="Widget.Material.NumberPicker" parent="Widget.NumberPicker">
+ <item name="internalLayout">@layout/number_picker_with_selector_wheel</item>
+ <item name="solidColor">@color/transparent</item>
+ <item name="selectionDivider">@drawable/numberpicker_selection_divider</item>
+ <item name="selectionDividerHeight">2dip</item>
+ <item name="selectionDividersDistance">48dip</item>
+ <item name="internalMinWidth">64dip</item>
+ <item name="internalMaxHeight">180dip</item>
+ <item name="virtualButtonPressedDrawable">?attr/selectableItemBackground</item>
+ </style>
+
+ <style name="Widget.Material.TimePicker" parent="Widget.TimePicker">
+ <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
+ <item name="internalLayout">@layout/time_picker_holo</item>
+ <item name="disabledColor">@color/bright_foreground_disabled_material_dark</item>
+ <item name="headerSelectedTextColor">?attr/colorControlActivated</item>
+ <item name="headerUnselectedTextColor">?attr/textColorPrimary</item>
+ <item name="headerBackgroundColor">@color/transparent</item>
+ <item name="numbersTextColor">?attr/textColorSecondary</item>
+ <item name="numbersBackgroundColor">@color/transparent</item>
+ <item name="amPmTextColor">?attr/textColorSecondary</item>
+ <item name="amPmUnselectedBackgroundColor">@color/transparent</item>
+ <item name="amPmSelectedBackgroundColor">?attr/colorControlActivated</item>
+ <item name="numbersSelectorColor">?attr/colorControlActivated</item>
+ </style>
+
+ <style name="Widget.Material.DatePicker" parent="Widget.DatePicker">
+ <item name="internalLayout">@layout/date_picker_holo</item>
+ <item name="calendarViewShown">true</item>
+ </style>
+
+ <style name="Widget.Material.ActivityChooserView" parent="Widget.ActivityChooserView">
+ <item name="background">@drawable/ab_share_pack_material</item>
+ </style>
+
+ <style name="Widget.Material.ImageWell" parent="Widget.ImageWell"/>
+
+ <style name="Widget.Material.ListView" parent="Widget.ListView">
+ <item name="divider">?attr/listDivider</item>
+ <item name="listSelector">?attr/listChoiceBackgroundIndicator</item>
+ </style>
+
+ <style name="Widget.Material.ListView.DropDown"/>
+ <style name="Widget.Material.ListView.White"/>
+
+ <style name="Widget.Material.PopupWindow" parent="Widget.PopupWindow"/>
+
+ <style name="Widget.Material.PopupWindow.ActionMode">
+ <item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupAnimationStyle">@style/Animation.PopupWindow.ActionMode</item>
+ </style>
+
+ <style name="Widget.Material.ProgressBar" parent="Widget.ProgressBar">
+ <item name="indeterminateDrawable">@drawable/progress_medium_material</item>
+ </style>
+
+ <style name="Widget.Material.ProgressBar.Inverse"/>
+
+ <style name="Widget.Material.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal">
+ <item name="progressDrawable">@drawable/progress_horizontal_material</item>
+ <item name="indeterminateDrawable">@drawable/progress_indeterminate_horizontal_holo</item>
+ <item name="minHeight">16dip</item>
+ <item name="maxHeight">16dip</item>
+ </style>
+
+ <style name="Widget.Material.ProgressBar.Small" parent="Widget.ProgressBar.Small">
+ <item name="indeterminateDrawable">@drawable/progress_small_material</item>
+ </style>
+
+ <style name="Widget.Material.ProgressBar.Small.Inverse"/>
+ <style name="Widget.Material.ProgressBar.Small.Title"/>
+
+ <style name="Widget.Material.ProgressBar.Large" parent="Widget.ProgressBar.Large">
+ <item name="indeterminateDrawable">@drawable/progress_large_material</item>
+ </style>
+
+ <style name="Widget.Material.ProgressBar.Large.Inverse"/>
+
+ <style name="Widget.Material.SeekBar">
+ <item name="indeterminateOnly">false</item>
+ <item name="progressDrawable">@drawable/scrubber_progress_horizontal_material</item>
+ <item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_material</item>
+ <item name="thumb">@drawable/scrubber_control_material_anim</item>
+ <item name="splitTrack">true</item>
+ <item name="focusable">true</item>
+ <item name="paddingStart">16dip</item>
+ <item name="paddingEnd">16dip</item>
+ <item name="mirrorForRtl">true</item>
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
+ </style>
+
+ <style name="Widget.Material.RatingBar" parent="Widget.RatingBar">
+ <item name="progressDrawable">@drawable/ratingbar_full_material</item>
+ <item name="indeterminateDrawable">@drawable/ratingbar_full_material</item>
+ </style>
+
+ <style name="Widget.Material.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
+ <item name="progressDrawable">@drawable/ratingbar_holo_dark</item>
+ <item name="indeterminateDrawable">@drawable/ratingbar_holo_dark</item>
+ <item name="minHeight">35dip</item>
+ <item name="maxHeight">35dip</item>
+ </style>
+
+ <style name="Widget.Material.RatingBar.Small" parent="Widget.RatingBar.Small">
+ <item name="progressDrawable">@drawable/ratingbar_small_holo_dark</item>
+ <item name="indeterminateDrawable">@drawable/ratingbar_small_holo_dark</item>
+ <item name="minHeight">16dip</item>
+ <item name="maxHeight">16dip</item>
+ </style>
+
+ <style name="Widget.Material.ScrollView" parent="Widget.ScrollView"/>
+ <style name="Widget.Material.HorizontalScrollView" parent="Widget.HorizontalScrollView"/>
+
+ <style name="Widget.Material.Spinner" parent="Widget.Spinner.DropDown">
+ <item name="background">@drawable/spinner_background_material</item>
+ <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
+ <item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="dropDownVerticalOffset">0dip</item>
+ <item name="dropDownHorizontalOffset">0dip</item>
+ <item name="dropDownWidth">wrap_content</item>
+ <item name="popupPromptView">@layout/simple_dropdown_hint</item>
+ <item name="gravity">start|center_vertical</item>
+ <item name="disableChildrenWhenDisabled">true</item>
+ </style>
+
+ <style name="Widget.Material.Spinner.DropDown"/>
+
+ <style name="Widget.Material.Spinner.DropDown.ActionBar">
+ <item name="background">@drawable/spinner_background_material</item>
+ <item name="overlapAnchor">true</item>
+ </style>
+
+ <style name="Widget.Material.TabWidget" parent="Widget.TabWidget">
+ <item name="tabStripLeft">@null</item>
+ <item name="tabStripRight">@null</item>
+ <item name="tabStripEnabled">false</item>
+ <item name="divider">?attr/dividerVertical</item>
+ <item name="showDividers">middle</item>
+ <item name="dividerPadding">8dip</item>
+ <item name="measureWithLargestChild">true</item>
+ <item name="tabLayout">@layout/tab_indicator_material</item>
+ </style>
+
+ <style name="Widget.Material.Tab" parent="Widget.Material.ActionBar.TabView">
+ <item name="background">@drawable/tab_indicator_material</item>
+ <item name="layout_width">0dip</item>
+ <item name="layout_weight">1</item>
+ <item name="minWidth">80dip</item>
+ </style>
+
+ <style name="Widget.Material.TabText" parent="Widget.Material.ActionBar.TabText">
+ <item name="maxWidth">180dip</item>
+ </style>
+
+ <style name="Widget.Material.Toolbar" parent="Widget.Toolbar">
+ <item name="android:navigationButtonStyle">@android:style/Widget.Material.Toolbar.Button.Navigation</item>
+ </style>
+
+ <style name="Widget.Material.Toolbar.Button.Navigation" parent="Widget.Toolbar.Button.Navigation">
+ <item name="android:background">?attr/selectableItemBackgroundBorderless</item>
+ </style>
+
+ <style name="Widget.Material.WebTextView" parent="Widget.WebTextView"/>
+
+ <style name="Widget.Material.WebView" parent="Widget.WebView"/>
+
+ <style name="Widget.Material.DropDownItem" parent="Widget.DropDownItem">
+ <item name="textAppearance">@style/TextAppearance.Material.Widget.DropDownItem</item>
+ <item name="paddingStart">8dp</item>
+ <item name="paddingEnd">8dp</item>
+ </style>
+
+ <style name="Widget.Material.DropDownItem.Spinner"/>
+
+ <style name="Widget.Material.KeyboardView" parent="Widget.KeyboardView"/>
+ <style name="Widget.Material.QuickContactBadge.WindowSmall" parent="Widget.QuickContactBadge.WindowSmall"/>
+ <style name="Widget.Material.QuickContactBadge.WindowMedium" parent="Widget.QuickContactBadge.WindowMedium"/>
+ <style name="Widget.Material.QuickContactBadge.WindowLarge" parent="Widget.QuickContactBadge.WindowLarge"/>
+ <style name="Widget.Material.QuickContactBadgeSmall.WindowSmall" parent="Widget.QuickContactBadgeSmall.WindowSmall"/>
+ <style name="Widget.Material.QuickContactBadgeSmall.WindowMedium" parent="Widget.QuickContactBadgeSmall.WindowMedium"/>
+ <style name="Widget.Material.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge"/>
+
+ <style name="Widget.Material.ListPopupWindow" parent="Widget.ListPopupWindow">
+ <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
+ <item name="popupBackground">@drawable/popup_background_material</item>
+ <item name="popupAnimationStyle">@style/Animation.Material.Popup</item>
+ <item name="dropDownVerticalOffset">0dip</item>
+ <item name="dropDownHorizontalOffset">0dip</item>
+ <item name="dropDownWidth">wrap_content</item>
+ </style>
+
+ <style name="Widget.Material.PopupMenu" parent="Widget.Material.ListPopupWindow"/>
+
+ <style name="Widget.Material.PopupMenu.Overflow">
+ <item name="overlapAnchor">true</item>
+ </style>
+
+ <style name="Widget.Material.ActionButton" parent="Widget.ActionButton">
+ <item name="minWidth">@dimen/action_button_min_width_material</item>
+ <item name="minHeight">@dimen/action_button_min_height_material</item>
+ <item name="gravity">center</item>
+ <item name="scaleType">center</item>
+ <item name="maxLines">2</item>
+ </style>
+
+ <style name="Widget.Material.ActionButton.CloseMode">
+ <item name="background">@drawable/btn_cab_done_material</item>
+ </style>
+
+ <style name="Widget.Material.ActionButton.Overflow">
+ <item name="src">@drawable/ic_menu_moreoverflow_material</item>
+ <item name="background">?attr/actionBarItemBackground</item>
+ <item name="contentDescription">@string/action_menu_overflow_description</item>
+ <item name="minWidth">@dimen/action_overflow_min_width_material</item>
+ <item name="minHeight">@dimen/action_button_min_height_material</item>
+ <item name="scaleType">center</item>
+ </style>
+
+ <style name="Widget.Material.ActionBar.TabView" parent="Widget.ActionBar.TabView">
+ <item name="background">@drawable/tab_indicator_material</item>
+ <item name="paddingStart">16dip</item>
+ <item name="paddingEnd">16dip</item>
+ </style>
+
+ <style name="Widget.Material.ActionBar.TabBar" parent="Widget.ActionBar.TabBar">
+ <item name="divider">?attr/actionBarDivider</item>
+ <item name="showDividers">middle</item>
+ <item name="dividerPadding">12dip</item>
+ </style>
+
+ <style name="Widget.Material.ActionBar.TabText" parent="Widget.ActionBar.TabText">
+ <item name="textAppearance">@style/TextAppearance.Material.Medium</item>
+ <item name="textColor">?attr/textColorPrimary</item>
+ <item name="textSize">12sp</item>
+ <item name="textStyle">bold</item>
+ <item name="textAllCaps">true</item>
+ <item name="ellipsize">marquee</item>
+ <item name="maxLines">2</item>
+ </style>
+
+ <style name="Widget.Material.ActionBar" parent="Widget.ActionBar">
+ <item name="background">@null</item>
+ <item name="backgroundStacked">@null</item>
+ <item name="backgroundSplit">@null</item>
+ <item name="displayOptions">showTitle</item>
+ <item name="divider">?attr/dividerVertical</item>
+ <item name="titleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Title</item>
+ <item name="subtitleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Subtitle</item>
+ <item name="progressBarStyle">@style/Widget.Material.ProgressBar.Horizontal</item>
+ <item name="indeterminateProgressStyle">@style/Widget.Material.ProgressBar</item>
+ <item name="progressBarPadding">32dip</item>
+ <item name="itemPadding">8dip</item>
+ <item name="homeLayout">@layout/action_bar_home_material</item>
+ <item name="gravity">center_vertical</item>
+ <item name="contentInsetStart">16dp</item>
+ </style>
+
+ <style name="Widget.Material.ActionBar.Solid">
+ <item name="background">?attr/colorPrimary</item>
+ <item name="backgroundStacked">?attr/colorPrimary</item>
+ <item name="backgroundSplit">?attr/colorPrimary</item>
+ </style>
+
+ <style name="Widget.Material.ActionMode" parent="Widget.ActionMode">
+ <item name="titleTextStyle">@style/TextAppearance.Material.Widget.ActionMode.Title</item>
+ <item name="subtitleTextStyle">@style/TextAppearance.Material.Widget.ActionMode.Subtitle</item>
+ </style>
+
+ <style name="Widget.Material.FastScroll" parent="Widget.FastScroll">
+ <item name="thumbMinWidth">0dp</item>
+ <item name="thumbMinHeight">0dp</item>
+ </style>
+
+ <style name="Widget.Material.PreferenceFrameLayout">
+ <item name="borderTop">0dip</item>
+ <item name="borderBottom">@dimen/preference_fragment_padding_bottom</item>
+ <item name="borderLeft">?attr/preferenceFragmentPaddingSide</item>
+ <item name="borderRight">?attr/preferenceFragmentPaddingSide</item>
+ </style>
+
+ <style name="Widget.Material.MediaRouteButton">
+ <item name="background">?attr/selectableItemBackgroundBorderless</item>
+ <item name="externalRouteEnabledDrawable">@drawable/ic_media_route_material</item>
+ <item name="minWidth">56dp</item>
+ <item name="minHeight">48dp</item>
+ <item name="focusable">true</item>
+ <item name="contentDescription">@string/media_route_button_content_description</item>
+ </style>
+
+ <!-- Light widget styles -->
+
+ <style name="Widget.Material.Light" parent="Widget.Material"/>
+ <style name="Widget.Material.Light.Button" parent="Widget.Material.Button"/>
+ <style name="Widget.Material.Light.Button.Small" parent="Widget.Material.Button.Small"/>
+ <style name="Widget.Material.Light.Button.Borderless" parent="Widget.Material.Button.Borderless"/>
+ <style name="Widget.Material.Light.Button.Borderless.Small" parent="Widget.Material.Button.Borderless.Small"/>
+ <style name="Widget.Material.Light.Button.Inset" parent="Widget.Material.Button.Inset"/>
+ <style name="Widget.Material.Light.Button.Toggle" parent="Widget.Material.Button.Toggle" />
+ <style name="Widget.Material.Light.ButtonBar" parent="Widget.Material.ButtonBar"/>
+ <style name="Widget.Material.Light.ButtonBar.AlertDialog" parent="Widget.Material.ButtonBar.AlertDialog"/>
+
+ <style name="Widget.Material.Light.SegmentedButton" parent="Widget.Material.SegmentedButton">
+ <item name="background">@drawable/btn_group_holo_light</item>
+ </style>
+
+ <style name="Widget.Material.Light.StackView" parent="Widget.Material.StackView"/>
+ <style name="Widget.Material.Light.TextView" parent="Widget.Material.TextView"/>
+ <style name="Widget.Material.Light.TextView.ListSeparator" parent="Widget.Material.TextView.ListSeparator"/>
+ <style name="Widget.Material.Light.TextView.SpinnerItem" parent="Widget.Material.TextView.SpinnerItem"/>
+ <style name="Widget.Material.Light.CheckedTextView" parent="Widget.Material.CheckedTextView"/>
+ <style name="Widget.Material.Light.TextSelectHandle" parent="Widget.Material.TextSelectHandle"/>
+ <style name="Widget.Material.Light.TextSuggestionsPopupWindow" parent="Widget.Material.TextSuggestionsPopupWindow"/>
+ <style name="Widget.Material.Light.AbsListView" parent="Widget.Material.AbsListView"/>
+ <style name="Widget.Material.Light.AutoCompleteTextView" parent="Widget.Material.AutoCompleteTextView" />
+ <style name="Widget.Material.Light.CompoundButton" parent="Widget.Material.CompoundButton"/>
+ <style name="Widget.Material.Light.CompoundButton.CheckBox" parent="Widget.Material.CompoundButton.CheckBox"/>
+ <style name="Widget.Material.Light.CompoundButton.RadioButton" parent="Widget.Material.CompoundButton.RadioButton"/>
+ <style name="Widget.Material.Light.CompoundButton.Star" parent="Widget.Material.CompoundButton.Star"/>
+ <style name="Widget.Material.Light.CompoundButton.Switch" parent="Widget.Material.CompoundButton.Switch" />
+ <style name="Widget.Material.Light.ListView.DropDown" parent="Widget.Material.ListView.DropDown"/>
+ <style name="Widget.Material.Light.EditText" parent="Widget.Material.EditText"/>
+ <style name="Widget.Material.Light.ExpandableListView" parent="Widget.Material.ExpandableListView"/>
+ <style name="Widget.Material.Light.ExpandableListView.White" parent="Widget.Material.ExpandableListView.White"/>
+ <style name="Widget.Material.Light.Gallery" parent="Widget.Material.Gallery"/>
+ <style name="Widget.Material.Light.GestureOverlayView" parent="Widget.Material.GestureOverlayView"/>
+ <style name="Widget.Material.Light.GridView" parent="Widget.Material.GridView"/>
+ <style name="Widget.Material.Light.ImageButton" parent="Widget.Material.ImageButton"/>
+
+ <style name="Widget.Material.Light.CalendarView" parent="Widget.CalendarView">
+ <item name="selectedWeekBackgroundColor">#330066ff</item>
+ <item name="focusedMonthDateColor">#FF000000</item>
+ <item name="unfocusedMonthDateColor">#7F08002B</item>
+ <item name="weekNumberColor">#7F080021</item>
+ <item name="weekSeparatorLineColor">#7F08002A</item>
+ <item name="weekDayTextAppearance">@style/TextAppearance.Material.CalendarViewWeekDayView</item>
+ </style>
+
+ <style name="Widget.Material.Light.NumberPicker" parent="Widget.Material.NumberPicker"/>
+
+ <style name="Widget.Material.Light.TimePicker" parent="Widget.Material.TimePicker">
+ <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
+ <item name="internalLayout">@layout/time_picker_holo</item>
+ <item name="disabledColor">@color/bright_foreground_disabled_material_light</item>
+ </style>
+
+ <style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker"/>
+ <style name="Widget.Material.Light.ActivityChooserView" parent="Widget.Material.ActivityChooserView" />
+ <style name="Widget.Material.Light.ImageWell" parent="Widget.Material.ImageWell"/>
+ <style name="Widget.Material.Light.ListView" parent="Widget.Material.ListView"/>
+ <style name="Widget.Material.Light.ListView.White" parent="Widget.Material.ListView.White"/>
+ <style name="Widget.Material.Light.PopupWindow" parent="Widget.Material.PopupWindow"/>
+ <style name="Widget.Material.Light.PopupWindow.ActionMode" parent="Widget.Material.PopupWindow.ActionMode"/>
+ <style name="Widget.Material.Light.ProgressBar" parent="Widget.Material.ProgressBar"/>
+ <style name="Widget.Material.Light.ProgressBar.Horizontal" parent="Widget.Material.ProgressBar.Horizontal"/>
+ <style name="Widget.Material.Light.ProgressBar.Small" parent="Widget.Material.ProgressBar.Small"/>
+ <style name="Widget.Material.Light.ProgressBar.Small.Title" parent="Widget.Material.ProgressBar.Small.Title"/>
+ <style name="Widget.Material.Light.ProgressBar.Large" parent="Widget.Material.ProgressBar.Large"/>
+ <style name="Widget.Material.Light.ProgressBar.Inverse" parent="Widget.Material.ProgressBar.Inverse"/>
+ <style name="Widget.Material.Light.ProgressBar.Small.Inverse" parent="Widget.Material.ProgressBar.Small.Inverse"/>
+ <style name="Widget.Material.Light.ProgressBar.Large.Inverse" parent="Widget.Material.ProgressBar.Large.Inverse"/>
+ <style name="Widget.Material.Light.SeekBar" parent="Widget.Material.SeekBar"/>
+ <style name="Widget.Material.Light.RatingBar" parent="Widget.Material.RatingBar" />
+
+ <style name="Widget.Material.Light.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
+ <item name="progressDrawable">@drawable/ratingbar_holo_light</item>
+ <item name="indeterminateDrawable">@drawable/ratingbar_holo_light</item>
+ <item name="minHeight">35dip</item>
+ <item name="maxHeight">35dip</item>
+ </style>
+
+ <style name="Widget.Material.Light.RatingBar.Small" parent="Widget.RatingBar.Small">
+ <item name="progressDrawable">@drawable/ratingbar_small_holo_light</item>
+ <item name="indeterminateDrawable">@drawable/ratingbar_small_holo_light</item>
+ <item name="minHeight">16dip</item>
+ <item name="maxHeight">16dip</item>
+ </style>
+
+ <style name="Widget.Material.Light.ScrollView" parent="Widget.Material.ScrollView"/>
+ <style name="Widget.Material.Light.HorizontalScrollView" parent="Widget.Material.HorizontalScrollView"/>
+ <style name="Widget.Material.Light.Spinner" parent="Widget.Material.Spinner" />
+ <style name="Widget.Material.Light.Spinner.DropDown" parent="Widget.Material.Spinner.DropDown"/>
+ <style name="Widget.Material.Light.Spinner.DropDown.ActionBar" parent="Widget.Material.Spinner.DropDown.ActionBar"/>
+ <style name="Widget.Material.Light.TabWidget" parent="Widget.Material.TabWidget"/>
+ <style name="Widget.Material.Light.WebTextView" parent="Widget.Material.WebTextView"/>
+ <style name="Widget.Material.Light.WebView" parent="Widget.Material.WebView"/>
+ <style name="Widget.Material.Light.DropDownItem" parent="Widget.Material.DropDownItem"/>
+ <style name="Widget.Material.Light.DropDownItem.Spinner" parent="Widget.Material.DropDownItem.Spinner"/>
+ <style name="Widget.Material.Light.KeyboardView" parent="Widget.Material.KeyboardView"/>
+ <style name="Widget.Material.Light.QuickContactBadge.WindowSmall" parent="Widget.Material.QuickContactBadge.WindowSmall"/>
+ <style name="Widget.Material.Light.QuickContactBadge.WindowMedium" parent="Widget.Material.QuickContactBadge.WindowMedium"/>
+ <style name="Widget.Material.Light.QuickContactBadge.WindowLarge" parent="Widget.Material.QuickContactBadge.WindowLarge"/>
+ <style name="Widget.Material.Light.QuickContactBadgeSmall.WindowSmall" parent="Widget.Material.QuickContactBadgeSmall.WindowSmall"/>
+ <style name="Widget.Material.Light.QuickContactBadgeSmall.WindowMedium" parent="Widget.Material.QuickContactBadgeSmall.WindowMedium"/>
+ <style name="Widget.Material.Light.QuickContactBadgeSmall.WindowLarge" parent="Widget.Material.QuickContactBadgeSmall.WindowLarge"/>
+ <style name="Widget.Material.Light.ListPopupWindow" parent="Widget.Material.ListPopupWindow"/>
+ <style name="Widget.Material.Light.PopupMenu" parent="Widget.Material.ListPopupWindow"/>
+ <style name="Widget.Material.Light.PopupMenu.Overflow" parent="Widget.Material.PopupMenu.Overflow"/>
+ <style name="Widget.Material.Light.ActionButton" parent="Widget.Material.ActionButton"/>
+ <style name="Widget.Material.Light.ActionButton.Overflow" parent="Widget.Material.ActionButton.Overflow"/>
+ <style name="Widget.Material.Light.Tab" parent="Widget.Material.Tab"/>
+ <style name="Widget.Material.Light.ActionBar.TabView" parent="Widget.Material.ActionBar.TabView"/>
+ <style name="Widget.Material.Light.ActionBar.TabBar" parent="Widget.Material.ActionBar.TabBar"/>
+ <style name="Widget.Material.Light.ActionBar.TabText" parent="Widget.Material.ActionBar.TabText"/>
+ <style name="Widget.Material.Light.ActionMode" parent="Widget.Material.ActionMode" />
+ <style name="Widget.Material.Light.ActionButton.CloseMode" parent="Widget.Material.ActionButton.CloseMode" />
+
+ <style name="Widget.Material.Light.ActionBar" parent="Widget.Material.ActionBar">
+ <item name="titleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Title</item>
+ <item name="subtitleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Subtitle</item>
+ <item name="background">@null</item>
+ <item name="backgroundStacked">@null</item>
+ <item name="backgroundSplit">@null</item>
+ <item name="homeAsUpIndicator">@drawable/ic_ab_back_material</item>
+ <item name="progressBarStyle">@style/Widget.Material.Light.ProgressBar.Horizontal</item>
+ <item name="indeterminateProgressStyle">@style/Widget.Material.Light.ProgressBar</item>
+ </style>
+
+ <style name="Widget.Material.Light.ActionBar.Solid">
+ <item name="background">?attr/colorPrimary</item>
+ <item name="backgroundStacked">?attr/colorPrimary</item>
+ <item name="backgroundSplit">?attr/colorPrimary</item>
+ </style>
+
+ <style name="Widget.Material.Light.FastScroll" parent="Widget.Material.FastScroll"/>
+ <style name="Widget.Material.Light.MediaRouteButton" parent="Widget.Material.MediaRouteButton" />
+
+ <!-- Animation Styles -->
+
+ <style name="Animation.Material" parent="Animation"/>
+ <style name="Animation.Material.Activity" parent="Animation.Activity"/>
+
+ <style name="Animation.Material.Dialog">
+ <item name="windowEnterAnimation">@anim/popup_enter_material</item>
+ <item name="windowExitAnimation">@anim/popup_exit_material</item>
+ </style>
+
+ <style name="Animation.Material.Popup">
+ <item name="windowEnterAnimation">@anim/popup_enter_material</item>
+ <item name="windowExitAnimation">@anim/popup_exit_material</item>
+ </style>
+
+ <!-- Dialog styles -->
+
+ <style name="AlertDialog.Material" parent="AlertDialog">
+ <item name="fullDark">@color/transparent</item>
+ <item name="topDark">@color/transparent</item>
+ <item name="centerDark">@color/transparent</item>
+ <item name="bottomDark">@color/transparent</item>
+ <item name="fullBright">@color/transparent</item>
+ <item name="topBright">@color/transparent</item>
+ <item name="centerBright">@color/transparent</item>
+ <item name="bottomBright">@color/transparent</item>
+ <item name="bottomMedium">@color/transparent</item>
+ <item name="centerMedium">@color/transparent</item>
+ <item name="layout">@layout/alert_dialog_material</item>
+ <item name="listLayout">@layout/select_dialog_material</item>
+ <item name="progressLayout">@layout/progress_dialog_material</item>
+ <item name="horizontalProgressLayout">@layout/alert_dialog_progress_material</item>
+ <item name="listItemLayout">@layout/select_dialog_item_material</item>
+ <item name="multiChoiceItemLayout">@layout/select_dialog_multichoice_material</item>
+ <item name="singleChoiceItemLayout">@layout/select_dialog_singlechoice_material</item>
+ </style>
+
+ <style name="AlertDialog.Material.Light"/>
+
+ <!-- Window title -->
+ <style name="WindowTitleBackground.Material">
+ <item name="background">@null</item>
+ <item name="paddingStart">16dp</item>
+ <item name="paddingEnd">16dp</item>
+ <item name="paddingTop">16dp</item>
+ </style>
+
+ <style name="WindowTitle.Material">
+ <item name="singleLine">true</item>
+ <item name="textAppearance">@style/TextAppearance.Material.WindowTitle</item>
+ <item name="shadowRadius">0</item>
+ <item name="ellipsize">end</item>
+ <item name="textAlignment">viewStart</item>
+ </style>
+
+ <style name="DialogWindowTitle.Material">
+ <item name="maxLines">1</item>
+ <item name="scrollHorizontally">true</item>
+ <item name="textAppearance">@style/TextAppearance.Material.DialogWindowTitle</item>
+ </style>
+
+ <style name="DialogWindowTitle.Material.Light" />
+
+</resources>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
deleted file mode 100644
index 108334fa..0000000
--- a/core/res/res/values/styles_quantum.xml
+++ /dev/null
@@ -1,1016 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!--
-===============================================================
- PLEASE READ
-===============================================================
-
-The Quantum themes must not be modified in order to pass CTS.
-Many related themes and styles depend on other values defined in this file.
-If you would like to provide custom themes and styles for your device,
-please see styles_device_defaults.xml.
-
-===============================================================
- PLEASE READ
-===============================================================
- -->
-<resources>
- <!-- Preference styles -->
- <eat-comment/>
-
- <style name="Preference.Quantum">
- <item name="layout">@layout/preference_quantum</item>
- </style>
-
- <style name="PreferenceFragment.Quantum">
- <item name="paddingStart">@dimen/preference_fragment_padding_side</item>
- <item name="paddingEnd">@dimen/preference_fragment_padding_side</item>
- </style>
-
- <style name="Preference.Quantum.Information">
- <item name="layout">@layout/preference_information_quantum</item>
- <item name="enabled">false</item>
- <item name="shouldDisableView">false</item>
- </style>
-
- <style name="Preference.Quantum.Category">
- <item name="layout">@layout/preference_category_quantum</item>
- <!-- The title should not dim if the category is disabled, instead only the preference children should dim. -->
- <item name="shouldDisableView">false</item>
- <item name="selectable">false</item>
- </style>
-
- <style name="Preference.Quantum.CheckBoxPreference">
- <item name="widgetLayout">@layout/preference_widget_checkbox</item>
- </style>
-
- <style name="Preference.Quantum.SwitchPreference">
- <item name="widgetLayout">@layout/preference_widget_switch</item>
- <item name="switchTextOn">@string/capital_on</item>
- <item name="switchTextOff">@string/capital_off</item>
- </style>
-
- <style name="Preference.Quantum.PreferenceScreen"/>
-
- <style name="Preference.Quantum.DialogPreference">
- <item name="positiveButtonText">@string/ok</item>
- <item name="negativeButtonText">@string/cancel</item>
- </style>
-
- <style name="Preference.Quantum.DialogPreference.YesNoPreference">
- <item name="positiveButtonText">@string/yes</item>
- <item name="negativeButtonText">@string/no</item>
- </style>
-
- <style name="Preference.Quantum.DialogPreference.EditTextPreference">
- <item name="dialogLayout">@layout/preference_dialog_edittext</item>
- </style>
-
- <style name="Preference.Quantum.RingtonePreference">
- <item name="ringtoneType">ringtone</item>
- <item name="showSilent">true</item>
- <item name="showDefault">true</item>
- </style>
-
- <!-- Begin Quantum theme styles -->
-
- <!-- Text styles -->
-
- <style name="TextAppearance.Quantum">
- <item name="textColor">?textColorPrimary</item>
- <item name="textColorHint">?textColorHint</item>
- <item name="textColorHighlight">?textColorHighlight</item>
- <item name="textColorLink">?textColorLink</item>
- <item name="textSize">@dimen/text_size_body_1_quantum</item>
- <item name="fontFamily">@string/font_family_body_1_quantum</item>
- <item name="elegantTextHeight">true</item>
- </style>
-
- <style name="TextAppearance.Quantum.Display4">
- <item name="textSize">@dimen/text_size_display_4_quantum</item>
- <item name="fontFamily">@string/font_family_display_4_quantum</item>
- <item name="textColor">?textColorSecondary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Display3">
- <item name="textSize">@dimen/text_size_display_3_quantum</item>
- <item name="fontFamily">@string/font_family_display_3_quantum</item>
- <item name="textColor">?textColorSecondary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Display2">
- <item name="textSize">@dimen/text_size_display_2_quantum</item>
- <item name="fontFamily">@string/font_family_display_2_quantum</item>
- <item name="textColor">?textColorSecondary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Display1">
- <item name="textSize">@dimen/text_size_display_1_quantum</item>
- <item name="fontFamily">@string/font_family_display_1_quantum</item>
- <item name="textColor">?textColorSecondary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Headline">
- <item name="textSize">@dimen/text_size_headline_quantum</item>
- <item name="fontFamily">@string/font_family_headline_quantum</item>
- <item name="textColor">?textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Title">
- <item name="textSize">@dimen/text_size_title_quantum</item>
- <item name="fontFamily">@string/font_family_title_quantum</item>
- <item name="textColor">?textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Subhead">
- <item name="textSize">@dimen/text_size_subhead_quantum</item>
- <item name="fontFamily">@string/font_family_subhead_quantum</item>
- <item name="textColor">?textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Body2">
- <item name="textSize">@dimen/text_size_body_2_quantum</item>
- <item name="fontFamily">@string/font_family_body_2_quantum</item>
- <item name="textColor">?textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Body1">
- <item name="textSize">@dimen/text_size_body_1_quantum</item>
- <item name="fontFamily">@string/font_family_body_1_quantum</item>
- <item name="textColor">?textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Caption">
- <item name="textSize">@dimen/text_size_caption_quantum</item>
- <item name="fontFamily">@string/font_family_caption_quantum</item>
- <item name="textColor">?textColorSecondary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Menu">
- <item name="textSize">@dimen/text_size_menu_quantum</item>
- <item name="fontFamily">@string/font_family_menu_quantum</item>
- <item name="textColor">?textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Button">
- <item name="textSize">@dimen/text_size_button_quantum</item>
- <item name="fontFamily">@string/font_family_button_quantum</item>
- <item name="textAllCaps">true</item>
- <item name="textColor">?textColorPrimary</item>
- </style>
-
- <!-- Deprecated text styles -->
-
- <style name="TextAppearance.Quantum.Inverse">
- <item name="textColor">?attr/textColorPrimaryInverse</item>
- <item name="textColorHint">?attr/textColorHintInverse</item>
- <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
- <item name="textColorLink">?attr/textColorLinkInverse</item>
- </style>
-
- <style name="TextAppearance.Quantum.Large">
- <item name="textSize">@dimen/text_size_large_quantum</item>
- <item name="textColor">?attr/textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Large.Inverse">
- <item name="textColor">?attr/textColorPrimaryInverse</item>
- <item name="textColorHint">?attr/textColorHintInverse</item>
- <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
- <item name="textColorLink">?attr/textColorLinkInverse</item>
- </style>
-
- <style name="TextAppearance.Quantum.Medium">
- <item name="textSize">@dimen/text_size_medium_quantum</item>
- <item name="textColor">?attr/textColorSecondary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Medium.Inverse">
- <item name="textColor">?attr/textColorSecondaryInverse</item>
- <item name="textColorHint">?attr/textColorHintInverse</item>
- <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
- <item name="textColorLink">?attr/textColorLinkInverse</item>
- </style>
-
- <style name="TextAppearance.Quantum.Small">
- <item name="textSize">@dimen/text_size_small_quantum</item>
- <item name="textColor">?attr/textColorTertiary</item>
- </style>
-
- <style name="TextAppearance.Quantum.Small.Inverse">
- <item name="textColor">?attr/textColorTertiaryInverse</item>
- <item name="textColorHint">?attr/textColorHintInverse</item>
- <item name="textColorHighlight">?attr/textColorHighlightInverse</item>
- <item name="textColorLink">?attr/textColorLinkInverse</item>
- </style>
-
- <style name="TextAppearance.Quantum.SearchResult">
- </style>
-
- <style name="TextAppearance.Quantum.SearchResult.Title" parent="TextAppearance.Quantum.Title" />
- <style name="TextAppearance.Quantum.SearchResult.Subtitle" parent="TextAppearance.Quantum.Subhead" />
-
- <style name="TextAppearance.Quantum.Widget"/>
- <style name="TextAppearance.Quantum.Widget.Button" parent="TextAppearance.Quantum.Button" />
-
- <style name="TextAppearance.Quantum.Widget.EditText">
- <item name="textColor">?attr/textColorPrimaryInverse</item>
- <item name="textColorHint">?attr/textColorHintInverse</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.Switch">
- <item name="textSize">14sp</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.PopupMenu"/>
-
- <style name="TextAppearance.Quantum.Widget.PopupMenu.Large">
- <item name="fontFamily">@string/font_family_menu_quantum</item>
- <item name="textSize">@dimen/text_size_menu_quantum</item>>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.PopupMenu.Small">
- <item name="fontFamily">@string/font_family_menu_quantum</item>
- <item name="textSize">@dimen/text_size_menu_quantum</item>>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.DropDownHint">
- <item name="fontFamily">@string/font_family_menu_quantum</item>
- <item name="textSize">@dimen/text_size_menu_quantum</item>>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.IconMenu.Item" parent="TextAppearance.Quantum.Small">
- <item name="textColor">?attr/textColorPrimary</item>
- </style>
-
- <!-- This style is for smaller screens; values-xlarge defines a version
- for larger screens. -->
- <style name="TextAppearance.Quantum.Widget.TabWidget">
- <item name="textSize">14sp</item>
- <item name="textStyle">normal</item>
- <item name="textColor">@color/tab_indicator_text</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.TextView">
- <item name="textColor">?attr/textColorPrimaryDisableOnly</item>
- <item name="textColorHint">?attr/textColorHint</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.TextView.PopupMenu">
- <item name="fontFamily">@string/font_family_menu_quantum</item>
- <item name="textSize">@dimen/text_size_menu_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.TextView.SpinnerItem"/>
-
- <style name="TextAppearance.Quantum.Widget.DropDownItem">
- <item name="textColor">?attr/textColorPrimaryDisableOnly</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionMode"/>
-
- <style name="TextAppearance.Quantum.Widget.ActionMode.Title" parent="TextAppearance.Quantum.Medium">
- <item name="textSize">@dimen/action_bar_title_text_size_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionMode.Title.Inverse" parent="TextAppearance.Quantum.Medium.Inverse">
- <item name="textSize">@dimen/action_bar_title_text_size_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionMode.Subtitle" parent="TextAppearance.Quantum.Small">
- <item name="textSize">@dimen/action_bar_subtitle_text_size_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Quantum.Small.Inverse">
- <item name="textSize">@dimen/action_bar_subtitle_text_size_quantum</item>
- </style>
-
- <!-- Text styles with no light versions -->
-
- <style name="TextAppearance.Quantum.Widget.ActionBar.Title" parent="TextAppearance.Quantum.Medium">
- <item name="textSize">@dimen/action_bar_title_text_size_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Quantum.Medium.Inverse">
- <item name="textSize">@dimen/action_bar_title_text_size_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionBar.Subtitle" parent="TextAppearance.Quantum.Small">
- <item name="textSize">@dimen/action_bar_subtitle_text_size_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionBar.Subtitle.Inverse" parent="TextAppearance.Quantum.Small.Inverse">
- <item name="textSize">@dimen/action_bar_subtitle_text_size_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionBar.Menu" parent="TextAppearance.Quantum.Small">
- <item name="fontFamily">@string/font_family_menu_quantum</item>
- <item name="textSize">@dimen/text_size_menu_quantum</item>>
- <item name="textColor">?attr/actionMenuTextColor</item>
- <item name="textAllCaps">@bool/config_actionMenuItemAllCaps</item>
- </style>
-
- <style name="TextAppearance.Quantum.Widget.ActionBar.Menu.Inverse" parent="TextAppearance.Quantum.Small.Inverse">
- <item name="fontFamily">@string/font_family_menu_quantum</item>
- <item name="textSize">@dimen/text_size_menu_quantum</item>>
- <item name="textColor">?attr/actionMenuTextColor</item>
- <item name="textAllCaps">@bool/config_actionMenuItemAllCaps</item>
- </style>
-
- <style name="TextAppearance.Quantum.WindowTitle">
- <item name="textColor">?attr/textColorPrimary</item>
- <item name="fontFamily">@string/font_family_headline_quantum</item>
- <item name="textSize">@dimen/text_size_headline_quantum</item>
- </style>
-
- <style name="TextAppearance.Quantum.DialogWindowTitle">
- <item name="fontFamily">@string/font_family_headline_quantum</item>
- <item name="textSize">@dimen/text_size_headline_quantum</item>
- <item name="textColor">?attr/textColorPrimary</item>
- </style>
-
- <style name="TextAppearance.Quantum.CalendarViewWeekDayView" parent="TextAppearance.Quantum.Small">
- <item name="textStyle">bold</item>
- <item name="textColor">#505050</item>
- </style>
-
- <style name="TextAppearance.Quantum.TimePicker.TimeLabel" parent="TextAppearance.Quantum">
- <item name="textSize">@dimen/timepicker_time_label_size</item>
- <item name="textColor">?attr/textColorSecondary</item>
- </style>
-
- <style name="TextAppearance.Quantum.TimePicker.AmPmLabel" parent="TextAppearance.Quantum">
- <item name="textSize">@dimen/timepicker_ampm_label_size</item>
- <item name="textAllCaps">true</item>
- <item name="textColor">?attr/textColorSecondary</item>
- <item name="textStyle">bold</item>
- </style>
-
- <style name="TextAppearance.StatusBar.Quantum" />
-
- <style name="TextAppearance.StatusBar.Quantum.EventContent">
- <item name="android:textColor">#90000000</item>
- <item name="android:textSize">@dimen/notification_text_size</item>
- </style>
-
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Title">
- <item name="android:textColor">#DD000000</item>
- <item name="android:textSize">@dimen/notification_title_text_size</item>
- </style>
-
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Line2">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
- </style>
-
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Info">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
- </style>
-
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Time">
- <item name="android:textSize">@dimen/notification_subtext_size</item>
- </style>
-
- <style name="TextAppearance.StatusBar.Quantum.EventContent.Emphasis">
- <item name="android:textColor">#66000000</item>
- </style>
-
- <style name="Widget.StatusBar.Quantum.ProgressBar" parent="Widget.Quantum.Light.ProgressBar.Horizontal">
- <item name="android:progressDrawable">@drawable/notification_quantum_media_progress</item>
- </style>
-
- <!-- Widget Styles -->
-
- <style name="Quantum"/>
- <style name="Quantum.Light"/>
- <style name="Widget.Quantum" parent="Widget" />
-
- <!-- Bordered ink button -->
- <style name="Widget.Quantum.Button" parent="Widget.Button">
- <item name="background">@drawable/btn_default_quantum</item>
- <item name="textAppearance">?attr/textAppearanceButton</item>
- <item name="textColor">?attr/textColorPrimary</item>
- <item name="minHeight">48dip</item>
- <item name="minWidth">88dip</item>
-
- <!-- TODO: Turn this back on when we support inset drawable outlines. -->
- <!-- <item name="stateListAnimator">@anim/button_state_list_anim_quantum</item> -->
- </style>
-
- <!-- Small bordered ink button -->
- <style name="Widget.Quantum.Button.Small">
- <item name="minHeight">48dip</item>
- <item name="minWidth">48dip</item>
- </style>
-
- <!-- 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 -->
- <style name="Widget.Quantum.Button.Borderless.Small">
- <item name="minHeight">48dip</item>
- <item name="minWidth">48dip</item>
- </style>
-
- <style name="Widget.Quantum.Button.Inset">
- <item name="background">@drawable/button_inset</item>
- </style>
-
- <style name="Widget.Quantum.Button.Toggle">
- <item name="background">@drawable/btn_toggle_quantum</item>
- <item name="textOn">@string/capital_on</item>
- <item name="textOff">@string/capital_off</item>
- <item name="minHeight">48dip</item>
- </style>
-
- <style name="Widget.Quantum.ButtonBar">
- <item name="background">@null</item>
- </style>
-
- <style name="Widget.Quantum.ButtonBar.AlertDialog">
- <item name="background">@null</item>
- </style>
-
- <style name="Widget.Quantum.SegmentedButton" parent="SegmentedButton">
- <item name="background">@drawable/btn_group_holo_dark</item>
- </style>
-
- <style name="Widget.Quantum.StackView">
- <item name="resOutColor">@color/holo_blue_light</item>
- <item name="clickColor">@color/holo_blue_light</item>
- </style>
-
- <style name="Widget.Quantum.TextView" parent="Widget.TextView"/>
-
- <style name="Widget.Quantum.TextView.ListSeparator" parent="Widget.TextView.ListSeparator">
- <item name="background">@drawable/list_section_divider_quantum</item>
- <item name="textAllCaps">true</item>
- </style>
-
- <style name="Widget.Quantum.TextView.SpinnerItem" parent="Widget.TextView.SpinnerItem">
- <item name="textAppearance">@style/TextAppearance.Quantum.Widget.TextView.SpinnerItem</item>
- <item name="paddingStart">8dp</item>
- <item name="paddingEnd">8dp</item>
- </style>
-
- <style name="Widget.Quantum.CheckedTextView" parent="Widget.CheckedTextView" />
- <style name="Widget.Quantum.TextSelectHandle" parent="Widget.TextSelectHandle"/>
- <style name="Widget.Quantum.TextSuggestionsPopupWindow" parent="Widget.TextSuggestionsPopupWindow"/>
- <style name="Widget.Quantum.AbsListView" parent="Widget.AbsListView"/>
-
- <style name="Widget.Quantum.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
- <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
- <item name="popupBackground">@drawable/popup_background_quantum</item>
- </style>
-
- <style name="Widget.Quantum.CompoundButton" parent="Widget.CompoundButton"/>
-
- <style name="Widget.Quantum.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">
- <item name="background">?attr/selectableItemBackgroundBorderless</item>
- </style>
-
- <style name="Widget.Quantum.CompoundButton.RadioButton" parent="Widget.CompoundButton.RadioButton">
- <item name="background">?attr/selectableItemBackgroundBorderless</item>
- </style>
-
- <style name="Widget.Quantum.CompoundButton.Star" parent="Widget.CompoundButton.Star">
- <item name="button">@drawable/btn_star_quantum</item>
- <item name="background">?attr/selectableItemBackgroundBorderless</item>
- </style>
-
- <style name="Widget.Quantum.CompoundButton.Switch">
- <item name="track">@drawable/switch_track_quantum</item>
- <item name="thumb">@drawable/switch_thumb_quantum_anim</item>
- <item name="splitTrack">true</item>
- <item name="switchTextAppearance">@style/TextAppearance.Quantum.Widget.Switch</item>
- <item name="textOn"></item>
- <item name="textOff"></item>
- <item name="switchMinWidth">4dip</item>
- <item name="switchPadding">4dip</item>
- <item name="background">?attr/selectableItemBackgroundBorderless</item>
- </style>
-
- <style name="Widget.Quantum.EditText" parent="Widget.EditText"/>
-
- <style name="Widget.Quantum.ExpandableListView" parent="Widget.Quantum.ListView">
- <item name="groupIndicator">@drawable/expander_group_quantum</item>
- <item name="indicatorLeft">?attr/expandableListPreferredItemIndicatorLeft</item>
- <item name="indicatorRight">?attr/expandableListPreferredItemIndicatorRight</item>
- <item name="childDivider">?attr/listDivider</item>
- </style>
-
- <style name="Widget.Quantum.ExpandableListView.White"/>
- <style name="Widget.Quantum.Gallery" parent="Widget.Gallery"/>
- <style name="Widget.Quantum.GestureOverlayView" parent="Widget.GestureOverlayView"/>
-
- <style name="Widget.Quantum.GridView" parent="Widget.GridView">
- <item name="android:listSelector">?attr/selectableItemBackground</item>
- </style>
-
- <style name="Widget.Quantum.CalendarView" parent="Widget.CalendarView">
- <item name="selectedWeekBackgroundColor">#330099FF</item>
- <item name="focusedMonthDateColor">#FFFFFFFF</item>
- <item name="unfocusedMonthDateColor">#66FFFFFF</item>
- <item name="weekNumberColor">#33FFFFFF</item>
- <item name="weekSeparatorLineColor">#19FFFFFF</item>
- <item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
- <item name="weekDayTextAppearance">@style/TextAppearance.Quantum.CalendarViewWeekDayView</item>
- </style>
-
- <style name="Widget.Quantum.ImageButton" parent="Widget.ImageButton">
- <item name="background">@drawable/btn_default_quantum</item>
- </style>
-
- <style name="Widget.Quantum.NumberPicker" parent="Widget.NumberPicker">
- <item name="internalLayout">@layout/number_picker_with_selector_wheel</item>
- <item name="solidColor">@color/transparent</item>
- <item name="selectionDivider">@drawable/numberpicker_selection_divider</item>
- <item name="selectionDividerHeight">2dip</item>
- <item name="selectionDividersDistance">48dip</item>
- <item name="internalMinWidth">64dip</item>
- <item name="internalMaxHeight">180dip</item>
- <item name="virtualButtonPressedDrawable">?attr/selectableItemBackground</item>
- </style>
-
- <style name="Widget.Quantum.TimePicker" parent="Widget.TimePicker">
- <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
- <item name="internalLayout">@layout/time_picker_holo</item>
- <item name="disabledColor">@color/bright_foreground_disabled_quantum_dark</item>
- <item name="headerSelectedTextColor">?attr/colorControlActivated</item>
- <item name="headerUnselectedTextColor">?attr/textColorPrimary</item>
- <item name="headerBackgroundColor">?attr/colorBackground</item>
- <item name="numbersTextColor">?attr/textColorSecondary</item>
- <item name="numbersBackgroundColor">?attr/colorControlNormal</item>
- <item name="amPmTextColor">?attr/textColorSecondary</item>
- <item name="amPmUnselectedBackgroundColor">?attr/colorControlNormal</item>
- <item name="amPmSelectedBackgroundColor">?attr/colorControlActivated</item>
- <item name="numbersSelectorColor">?attr/colorControlActivated</item>
- </style>
-
- <style name="Widget.Quantum.DatePicker" parent="Widget.DatePicker">
- <item name="internalLayout">@layout/date_picker_holo</item>
- <item name="calendarViewShown">true</item>
- </style>
-
- <style name="Widget.Quantum.ActivityChooserView" parent="Widget.ActivityChooserView">
- <item name="background">@drawable/ab_share_pack_quantum</item>
- </style>
-
- <style name="Widget.Quantum.ImageWell" parent="Widget.ImageWell"/>
-
- <style name="Widget.Quantum.ListView" parent="Widget.ListView">
- <item name="divider">?attr/listDivider</item>
- <item name="listSelector">?attr/listChoiceBackgroundIndicator</item>
- </style>
-
- <style name="Widget.Quantum.ListView.DropDown"/>
- <style name="Widget.Quantum.ListView.White"/>
-
- <style name="Widget.Quantum.PopupWindow" parent="Widget.PopupWindow"/>
-
- <style name="Widget.Quantum.PopupWindow.ActionMode">
- <item name="popupBackground">@drawable/popup_background_quantum</item>
- <item name="popupAnimationStyle">@style/Animation.PopupWindow.ActionMode</item>
- </style>
-
- <style name="Widget.Quantum.ProgressBar" parent="Widget.ProgressBar">
- <item name="indeterminateDrawable">@drawable/progress_medium_holo</item>
- </style>
-
- <style name="Widget.Quantum.ProgressBar.Inverse"/>
-
- <style name="Widget.Quantum.ProgressBar.Horizontal" parent="Widget.ProgressBar.Horizontal">
- <item name="progressDrawable">@drawable/progress_horizontal_quantum</item>
- <item name="indeterminateDrawable">@drawable/progress_indeterminate_horizontal_holo</item>
- <item name="minHeight">16dip</item>
- <item name="maxHeight">16dip</item>
- </style>
-
- <style name="Widget.Quantum.ProgressBar.Small" parent="Widget.ProgressBar.Small">
- <item name="indeterminateDrawable">@drawable/progress_small_holo</item>
- </style>
-
- <style name="Widget.Quantum.ProgressBar.Small.Inverse"/>
- <style name="Widget.Quantum.ProgressBar.Small.Title"/>
-
- <style name="Widget.Quantum.ProgressBar.Large" parent="Widget.ProgressBar.Large">
- <item name="indeterminateDrawable">@drawable/progress_large_holo</item>
- </style>
-
- <style name="Widget.Quantum.ProgressBar.Large.Inverse"/>
-
- <style name="Widget.Quantum.SeekBar">
- <item name="indeterminateOnly">false</item>
- <item name="progressDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
- <item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
- <item name="thumb">@drawable/scrubber_control_quantum_anim</item>
- <item name="splitTrack">true</item>
- <item name="focusable">true</item>
- <item name="paddingStart">16dip</item>
- <item name="paddingEnd">16dip</item>
- <item name="mirrorForRtl">true</item>
- <item name="background">?attr/selectableItemBackgroundBorderless</item>
- </style>
-
- <style name="Widget.Quantum.RatingBar" parent="Widget.RatingBar">
- <item name="progressDrawable">@drawable/ratingbar_full_quantum</item>
- <item name="indeterminateDrawable">@drawable/ratingbar_full_quantum</item>
- </style>
-
- <style name="Widget.Quantum.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
- <item name="progressDrawable">@drawable/ratingbar_holo_dark</item>
- <item name="indeterminateDrawable">@drawable/ratingbar_holo_dark</item>
- <item name="minHeight">35dip</item>
- <item name="maxHeight">35dip</item>
- </style>
-
- <style name="Widget.Quantum.RatingBar.Small" parent="Widget.RatingBar.Small">
- <item name="progressDrawable">@drawable/ratingbar_small_holo_dark</item>
- <item name="indeterminateDrawable">@drawable/ratingbar_small_holo_dark</item>
- <item name="minHeight">16dip</item>
- <item name="maxHeight">16dip</item>
- </style>
-
- <style name="Widget.Quantum.ScrollView" parent="Widget.ScrollView"/>
- <style name="Widget.Quantum.HorizontalScrollView" parent="Widget.HorizontalScrollView"/>
-
- <style name="Widget.Quantum.Spinner" parent="Widget.Spinner.DropDown">
- <item name="background">@drawable/spinner_background_quantum</item>
- <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
- <item name="popupBackground">@drawable/popup_background_quantum</item>
- <item name="dropDownVerticalOffset">0dip</item>
- <item name="dropDownHorizontalOffset">0dip</item>
- <item name="dropDownWidth">wrap_content</item>
- <item name="popupPromptView">@layout/simple_dropdown_hint</item>
- <item name="gravity">start|center_vertical</item>
- <item name="disableChildrenWhenDisabled">true</item>
- </style>
-
- <style name="Widget.Quantum.Spinner.DropDown"/>
-
- <style name="Widget.Quantum.Spinner.DropDown.ActionBar">
- <item name="background">@drawable/spinner_background_quantum</item>
- </style>
-
- <style name="Widget.Quantum.TabWidget" parent="Widget.TabWidget">
- <item name="tabStripLeft">@null</item>
- <item name="tabStripRight">@null</item>
- <item name="tabStripEnabled">false</item>
- <item name="divider">?attr/dividerVertical</item>
- <item name="showDividers">middle</item>
- <item name="dividerPadding">8dip</item>
- <item name="measureWithLargestChild">true</item>
- <item name="tabLayout">@layout/tab_indicator_quantum</item>
- </style>
-
- <style name="Widget.Quantum.Tab" parent="Widget.Quantum.ActionBar.TabView">
- <item name="background">@drawable/tab_indicator_quantum</item>
- <item name="layout_width">0dip</item>
- <item name="layout_weight">1</item>
- <item name="minWidth">80dip</item>
- </style>
-
- <style name="Widget.Quantum.TabText" parent="Widget.Quantum.ActionBar.TabText">
- <item name="maxWidth">180dip</item>
- </style>
-
- <style name="Widget.Quantum.WebTextView" parent="Widget.WebTextView"/>
-
- <style name="Widget.Quantum.WebView" parent="Widget.WebView"/>
-
- <style name="Widget.Quantum.DropDownItem" parent="Widget.DropDownItem">
- <item name="textAppearance">@style/TextAppearance.Quantum.Widget.DropDownItem</item>
- <item name="paddingStart">8dp</item>
- <item name="paddingEnd">8dp</item>
- </style>
-
- <style name="Widget.Quantum.DropDownItem.Spinner"/>
-
- <style name="Widget.Quantum.KeyboardView" parent="Widget.KeyboardView"/>
- <style name="Widget.Quantum.QuickContactBadge.WindowSmall" parent="Widget.QuickContactBadge.WindowSmall"/>
- <style name="Widget.Quantum.QuickContactBadge.WindowMedium" parent="Widget.QuickContactBadge.WindowMedium"/>
- <style name="Widget.Quantum.QuickContactBadge.WindowLarge" parent="Widget.QuickContactBadge.WindowLarge"/>
- <style name="Widget.Quantum.QuickContactBadgeSmall.WindowSmall" parent="Widget.QuickContactBadgeSmall.WindowSmall"/>
- <style name="Widget.Quantum.QuickContactBadgeSmall.WindowMedium" parent="Widget.QuickContactBadgeSmall.WindowMedium"/>
- <style name="Widget.Quantum.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge"/>
-
- <style name="Widget.Quantum.ListPopupWindow" parent="Widget.ListPopupWindow">
- <item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
- <item name="popupBackground">@drawable/popup_background_quantum</item>
- <item name="popupAnimationStyle">@style/Animation.Quantum.Popup</item>
- <item name="dropDownVerticalOffset">0dip</item>
- <item name="dropDownHorizontalOffset">0dip</item>
- <item name="dropDownWidth">wrap_content</item>
- </style>
-
- <style name="Widget.Quantum.PopupMenu" parent="Widget.Quantum.ListPopupWindow"/>
-
- <style name="Widget.Quantum.PopupMenu.Overflow">
- <item name="overlapAnchor">true</item>
- </style>
-
- <style name="Widget.Quantum.ActionButton" parent="Widget.ActionButton">
- <item name="minWidth">@dimen/action_button_min_width_quantum</item>
- <item name="minHeight">@dimen/action_button_min_height_quantum</item>
- <item name="gravity">center</item>
- <item name="scaleType">center</item>
- <item name="maxLines">2</item>
- </style>
-
- <style name="Widget.Quantum.ActionButton.CloseMode">
- <item name="background">@drawable/btn_cab_done_quantum</item>
- </style>
-
- <style name="Widget.Quantum.ActionButton.Overflow">
- <item name="src">@drawable/ic_menu_moreoverflow_quantum</item>
- <item name="background">?attr/actionBarItemBackground</item>
- <item name="contentDescription">@string/action_menu_overflow_description</item>
- <item name="minWidth">@dimen/action_overflow_min_width_quantum</item>
- <item name="minHeight">@dimen/action_button_min_height_quantum</item>
- <item name="scaleType">center</item>
- </style>
-
- <style name="Widget.Quantum.ActionBar.TabView" parent="Widget.ActionBar.TabView">
- <item name="background">@drawable/tab_indicator_quantum</item>
- <item name="paddingStart">16dip</item>
- <item name="paddingEnd">16dip</item>
- </style>
-
- <style name="Widget.Quantum.ActionBar.TabBar" parent="Widget.ActionBar.TabBar">
- <item name="divider">?attr/actionBarDivider</item>
- <item name="showDividers">middle</item>
- <item name="dividerPadding">12dip</item>
- </style>
-
- <style name="Widget.Quantum.ActionBar.TabText" parent="Widget.ActionBar.TabText">
- <item name="textAppearance">@style/TextAppearance.Quantum.Medium</item>
- <item name="textColor">?attr/textColorPrimary</item>
- <item name="textSize">12sp</item>
- <item name="textStyle">bold</item>
- <item name="textAllCaps">true</item>
- <item name="ellipsize">marquee</item>
- <item name="maxLines">2</item>
- </style>
-
- <style name="Widget.Quantum.ActionBar" parent="Widget.ActionBar">
- <item name="background">@null</item>
- <item name="backgroundStacked">@null</item>
- <item name="backgroundSplit">@null</item>
- <item name="displayOptions">showTitle</item>
- <item name="divider">?attr/dividerVertical</item>
- <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Title</item>
- <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Subtitle</item>
- <item name="progressBarStyle">@style/Widget.Quantum.ProgressBar.Horizontal</item>
- <item name="indeterminateProgressStyle">@style/Widget.Quantum.ProgressBar</item>
- <item name="progressBarPadding">32dip</item>
- <item name="itemPadding">8dip</item>
- <item name="homeLayout">@layout/action_bar_home_quantum</item>
- <item name="gravity">center_vertical</item>
- </style>
-
- <style name="Widget.Quantum.ActionBar.Solid">
- <item name="background">?attr/colorPrimary</item>
- <item name="backgroundStacked">?attr/colorPrimary</item>
- <item name="backgroundSplit">?attr/colorPrimary</item>
- </style>
-
- <style name="Widget.Quantum.ActionMode" parent="Widget.ActionMode">
- <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionMode.Title</item>
- <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionMode.Subtitle</item>
- </style>
-
- <style name="Widget.Quantum.FastScroll" parent="Widget.FastScroll">
- <item name="thumbMinWidth">0dp</item>
- <item name="thumbMinHeight">0dp</item>
- </style>
-
- <style name="Widget.Quantum.PreferenceFrameLayout">
- <item name="borderTop">0dip</item>
- <item name="borderBottom">@dimen/preference_fragment_padding_bottom</item>
- <item name="borderLeft">?attr/preferenceFragmentPaddingSide</item>
- <item name="borderRight">?attr/preferenceFragmentPaddingSide</item>
- </style>
-
- <style name="Widget.Quantum.MediaRouteButton">
- <item name="background">?attr/selectableItemBackgroundBorderless</item>
- <item name="externalRouteEnabledDrawable">@drawable/ic_media_route_quantum</item>
- <item name="minWidth">56dp</item>
- <item name="minHeight">48dp</item>
- <item name="focusable">true</item>
- <item name="contentDescription">@string/media_route_button_content_description</item>
- </style>
-
- <!-- Light widget styles -->
-
- <style name="Widget.Quantum.Light" parent="Widget.Quantum"/>
- <style name="Widget.Quantum.Light.Button" parent="Widget.Quantum.Button"/>
- <style name="Widget.Quantum.Light.Button.Small" parent="Widget.Quantum.Button.Small"/>
- <style name="Widget.Quantum.Light.Button.Borderless" parent="Widget.Quantum.Button.Borderless"/>
- <style name="Widget.Quantum.Light.Button.Borderless.Small" parent="Widget.Quantum.Button.Borderless.Small"/>
- <style name="Widget.Quantum.Light.Button.Inset" parent="Widget.Quantum.Button.Inset"/>
- <style name="Widget.Quantum.Light.Button.Toggle" parent="Widget.Quantum.Button.Toggle" />
- <style name="Widget.Quantum.Light.ButtonBar" parent="Widget.Quantum.ButtonBar"/>
- <style name="Widget.Quantum.Light.ButtonBar.AlertDialog" parent="Widget.Quantum.ButtonBar.AlertDialog"/>
-
- <style name="Widget.Quantum.Light.SegmentedButton" parent="Widget.Quantum.SegmentedButton">
- <item name="background">@drawable/btn_group_holo_light</item>
- </style>
-
- <style name="Widget.Quantum.Light.StackView" parent="Widget.Quantum.StackView"/>
- <style name="Widget.Quantum.Light.TextView" parent="Widget.Quantum.TextView"/>
- <style name="Widget.Quantum.Light.TextView.ListSeparator" parent="Widget.Quantum.TextView.ListSeparator"/>
- <style name="Widget.Quantum.Light.TextView.SpinnerItem" parent="Widget.Quantum.TextView.SpinnerItem"/>
- <style name="Widget.Quantum.Light.CheckedTextView" parent="Widget.Quantum.CheckedTextView"/>
- <style name="Widget.Quantum.Light.TextSelectHandle" parent="Widget.Quantum.TextSelectHandle"/>
- <style name="Widget.Quantum.Light.TextSuggestionsPopupWindow" parent="Widget.Quantum.TextSuggestionsPopupWindow"/>
- <style name="Widget.Quantum.Light.AbsListView" parent="Widget.Quantum.AbsListView"/>
- <style name="Widget.Quantum.Light.AutoCompleteTextView" parent="Widget.Quantum.AutoCompleteTextView" />
- <style name="Widget.Quantum.Light.CompoundButton" parent="Widget.Quantum.CompoundButton"/>
- <style name="Widget.Quantum.Light.CompoundButton.CheckBox" parent="Widget.Quantum.CompoundButton.CheckBox"/>
- <style name="Widget.Quantum.Light.CompoundButton.RadioButton" parent="Widget.Quantum.CompoundButton.RadioButton"/>
- <style name="Widget.Quantum.Light.CompoundButton.Star" parent="Widget.Quantum.CompoundButton.Star"/>
- <style name="Widget.Quantum.Light.CompoundButton.Switch" parent="Widget.Quantum.CompoundButton.Switch" />
- <style name="Widget.Quantum.Light.ListView.DropDown" parent="Widget.Quantum.ListView.DropDown"/>
- <style name="Widget.Quantum.Light.EditText" parent="Widget.Quantum.EditText"/>
- <style name="Widget.Quantum.Light.ExpandableListView" parent="Widget.Quantum.ExpandableListView"/>
- <style name="Widget.Quantum.Light.ExpandableListView.White" parent="Widget.Quantum.ExpandableListView.White"/>
- <style name="Widget.Quantum.Light.Gallery" parent="Widget.Quantum.Gallery"/>
- <style name="Widget.Quantum.Light.GestureOverlayView" parent="Widget.Quantum.GestureOverlayView"/>
- <style name="Widget.Quantum.Light.GridView" parent="Widget.Quantum.GridView"/>
- <style name="Widget.Quantum.Light.ImageButton" parent="Widget.Quantum.ImageButton"/>
-
- <style name="Widget.Quantum.Light.CalendarView" parent="Widget.CalendarView">
- <item name="selectedWeekBackgroundColor">#330066ff</item>
- <item name="focusedMonthDateColor">#FF000000</item>
- <item name="unfocusedMonthDateColor">#7F08002B</item>
- <item name="weekNumberColor">#7F080021</item>
- <item name="weekSeparatorLineColor">#7F08002A</item>
- <item name="weekDayTextAppearance">@style/TextAppearance.Quantum.CalendarViewWeekDayView</item>
- </style>
-
- <style name="Widget.Quantum.Light.NumberPicker" parent="Widget.Quantum.NumberPicker"/>
-
- <style name="Widget.Quantum.Light.TimePicker" parent="Widget.Quantum.TimePicker">
- <item name="legacyLayout">@layout/time_picker_legacy_holo</item>
- <item name="internalLayout">@layout/time_picker_holo</item>
- <item name="disabledColor">@color/bright_foreground_disabled_quantum_light</item>
- </style>
-
- <style name="Widget.Quantum.Light.DatePicker" parent="Widget.Quantum.DatePicker"/>
- <style name="Widget.Quantum.Light.ActivityChooserView" parent="Widget.Quantum.ActivityChooserView" />
- <style name="Widget.Quantum.Light.ImageWell" parent="Widget.Quantum.ImageWell"/>
- <style name="Widget.Quantum.Light.ListView" parent="Widget.Quantum.ListView"/>
- <style name="Widget.Quantum.Light.ListView.White" parent="Widget.Quantum.ListView.White"/>
- <style name="Widget.Quantum.Light.PopupWindow" parent="Widget.Quantum.PopupWindow"/>
- <style name="Widget.Quantum.Light.PopupWindow.ActionMode" parent="Widget.Quantum.PopupWindow.ActionMode"/>
- <style name="Widget.Quantum.Light.ProgressBar" parent="Widget.Quantum.ProgressBar"/>
- <style name="Widget.Quantum.Light.ProgressBar.Horizontal" parent="Widget.Quantum.ProgressBar.Horizontal"/>
- <style name="Widget.Quantum.Light.ProgressBar.Small" parent="Widget.Quantum.ProgressBar.Small"/>
- <style name="Widget.Quantum.Light.ProgressBar.Small.Title" parent="Widget.Quantum.ProgressBar.Small.Title"/>
- <style name="Widget.Quantum.Light.ProgressBar.Large" parent="Widget.Quantum.ProgressBar.Large"/>
- <style name="Widget.Quantum.Light.ProgressBar.Inverse" parent="Widget.Quantum.ProgressBar.Inverse"/>
- <style name="Widget.Quantum.Light.ProgressBar.Small.Inverse" parent="Widget.Quantum.ProgressBar.Small.Inverse"/>
- <style name="Widget.Quantum.Light.ProgressBar.Large.Inverse" parent="Widget.Quantum.ProgressBar.Large.Inverse"/>
- <style name="Widget.Quantum.Light.SeekBar" parent="Widget.Quantum.SeekBar"/>
- <style name="Widget.Quantum.Light.RatingBar" parent="Widget.Quantum.RatingBar" />
-
- <style name="Widget.Quantum.Light.RatingBar.Indicator" parent="Widget.RatingBar.Indicator">
- <item name="progressDrawable">@drawable/ratingbar_holo_light</item>
- <item name="indeterminateDrawable">@drawable/ratingbar_holo_light</item>
- <item name="minHeight">35dip</item>
- <item name="maxHeight">35dip</item>
- </style>
-
- <style name="Widget.Quantum.Light.RatingBar.Small" parent="Widget.RatingBar.Small">
- <item name="progressDrawable">@drawable/ratingbar_small_holo_light</item>
- <item name="indeterminateDrawable">@drawable/ratingbar_small_holo_light</item>
- <item name="minHeight">16dip</item>
- <item name="maxHeight">16dip</item>
- </style>
-
- <style name="Widget.Quantum.Light.ScrollView" parent="Widget.Quantum.ScrollView"/>
- <style name="Widget.Quantum.Light.HorizontalScrollView" parent="Widget.Quantum.HorizontalScrollView"/>
- <style name="Widget.Quantum.Light.Spinner" parent="Widget.Quantum.Spinner" />
- <style name="Widget.Quantum.Light.Spinner.DropDown" parent="Widget.Quantum.Spinner.DropDown"/>
- <style name="Widget.Quantum.Light.Spinner.DropDown.ActionBar" parent="Widget.Quantum.Spinner.DropDown.ActionBar"/>
- <style name="Widget.Quantum.Light.TabWidget" parent="Widget.Quantum.TabWidget"/>
- <style name="Widget.Quantum.Light.WebTextView" parent="Widget.Quantum.WebTextView"/>
- <style name="Widget.Quantum.Light.WebView" parent="Widget.Quantum.WebView"/>
- <style name="Widget.Quantum.Light.DropDownItem" parent="Widget.Quantum.DropDownItem"/>
- <style name="Widget.Quantum.Light.DropDownItem.Spinner" parent="Widget.Quantum.DropDownItem.Spinner"/>
- <style name="Widget.Quantum.Light.KeyboardView" parent="Widget.Quantum.KeyboardView"/>
- <style name="Widget.Quantum.Light.QuickContactBadge.WindowSmall" parent="Widget.Quantum.QuickContactBadge.WindowSmall"/>
- <style name="Widget.Quantum.Light.QuickContactBadge.WindowMedium" parent="Widget.Quantum.QuickContactBadge.WindowMedium"/>
- <style name="Widget.Quantum.Light.QuickContactBadge.WindowLarge" parent="Widget.Quantum.QuickContactBadge.WindowLarge"/>
- <style name="Widget.Quantum.Light.QuickContactBadgeSmall.WindowSmall" parent="Widget.Quantum.QuickContactBadgeSmall.WindowSmall"/>
- <style name="Widget.Quantum.Light.QuickContactBadgeSmall.WindowMedium" parent="Widget.Quantum.QuickContactBadgeSmall.WindowMedium"/>
- <style name="Widget.Quantum.Light.QuickContactBadgeSmall.WindowLarge" parent="Widget.Quantum.QuickContactBadgeSmall.WindowLarge"/>
- <style name="Widget.Quantum.Light.ListPopupWindow" parent="Widget.Quantum.ListPopupWindow"/>
- <style name="Widget.Quantum.Light.PopupMenu" parent="Widget.Quantum.ListPopupWindow"/>
- <style name="Widget.Quantum.Light.PopupMenu.Overflow" parent="Widget.Quantum.PopupMenu.Overflow"/>
- <style name="Widget.Quantum.Light.ActionButton" parent="Widget.Quantum.ActionButton"/>
- <style name="Widget.Quantum.Light.ActionButton.Overflow" parent="Widget.Quantum.ActionButton.Overflow"/>
- <style name="Widget.Quantum.Light.Tab" parent="Widget.Quantum.Tab"/>
- <style name="Widget.Quantum.Light.ActionBar.TabView" parent="Widget.Quantum.ActionBar.TabView"/>
- <style name="Widget.Quantum.Light.ActionBar.TabBar" parent="Widget.Quantum.ActionBar.TabBar"/>
- <style name="Widget.Quantum.Light.ActionBar.TabText" parent="Widget.Quantum.ActionBar.TabText"/>
- <style name="Widget.Quantum.Light.ActionMode" parent="Widget.Quantum.ActionMode" />
- <style name="Widget.Quantum.Light.ActionButton.CloseMode" parent="Widget.Quantum.ActionButton.CloseMode" />
-
- <style name="Widget.Quantum.Light.ActionBar" parent="Widget.Quantum.ActionBar">
- <item name="titleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Title</item>
- <item name="subtitleTextStyle">@style/TextAppearance.Quantum.Widget.ActionBar.Subtitle</item>
- <item name="background">@null</item>
- <item name="backgroundStacked">@null</item>
- <item name="backgroundSplit">@null</item>
- <item name="homeAsUpIndicator">@drawable/ic_ab_back_quantum</item>
- <item name="progressBarStyle">@style/Widget.Quantum.Light.ProgressBar.Horizontal</item>
- <item name="indeterminateProgressStyle">@style/Widget.Quantum.Light.ProgressBar</item>
- </style>
-
- <style name="Widget.Quantum.Light.ActionBar.Solid">
- <item name="background">?attr/colorPrimary</item>
- <item name="backgroundStacked">?attr/colorPrimary</item>
- <item name="backgroundSplit">?attr/colorPrimary</item>
- </style>
-
- <style name="Widget.Quantum.Light.FastScroll" parent="Widget.Quantum.FastScroll"/>
- <style name="Widget.Quantum.Light.MediaRouteButton" parent="Widget.Quantum.MediaRouteButton" />
-
- <!-- Animation Styles -->
-
- <style name="Animation.Quantum" parent="Animation"/>
- <style name="Animation.Quantum.Activity" parent="Animation.Activity"/>
-
- <style name="Animation.Quantum.Dialog">
- <item name="windowEnterAnimation">@anim/popup_enter_quantum</item>
- <item name="windowExitAnimation">@anim/popup_exit_quantum</item>
- </style>
-
- <style name="Animation.Quantum.Popup">
- <item name="windowEnterAnimation">@anim/popup_enter_quantum</item>
- <item name="windowExitAnimation">@anim/popup_exit_quantum</item>
- </style>
-
- <!-- Dialog styles -->
-
- <style name="AlertDialog.Quantum" parent="AlertDialog">
- <item name="fullDark">@color/transparent</item>
- <item name="topDark">@color/transparent</item>
- <item name="centerDark">@color/transparent</item>
- <item name="bottomDark">@color/transparent</item>
- <item name="fullBright">@color/transparent</item>
- <item name="topBright">@color/transparent</item>
- <item name="centerBright">@color/transparent</item>
- <item name="bottomBright">@color/transparent</item>
- <item name="bottomMedium">@color/transparent</item>
- <item name="centerMedium">@color/transparent</item>
- <item name="layout">@layout/alert_dialog_quantum</item>
- <item name="listLayout">@layout/select_dialog_quantum</item>
- <item name="progressLayout">@layout/progress_dialog_quantum</item>
- <item name="horizontalProgressLayout">@layout/alert_dialog_progress_quantum</item>
- <item name="listItemLayout">@layout/select_dialog_item_quantum</item>
- <item name="multiChoiceItemLayout">@layout/select_dialog_multichoice_quantum</item>
- <item name="singleChoiceItemLayout">@layout/select_dialog_singlechoice_quantum</item>
- </style>
-
- <style name="AlertDialog.Quantum.Light"/>
-
- <!-- Window title -->
- <style name="WindowTitleBackground.Quantum">
- <item name="background">@null</item>
- </style>
-
- <style name="WindowTitle.Quantum">
- <item name="singleLine">true</item>
- <item name="textAppearance">@style/TextAppearance.Quantum.WindowTitle</item>
- <item name="shadowRadius">0</item>
- </style>
-
- <style name="DialogWindowTitle.Quantum">
- <item name="maxLines">1</item>
- <item name="scrollHorizontally">true</item>
- <item name="textAppearance">@style/TextAppearance.Quantum.DialogWindowTitle</item>
- </style>
-
- <style name="DialogWindowTitle.Quantum.Light">
- <item name="textAppearance">@style/TextAppearance.Quantum.DialogWindowTitle</item>
- </style>
-
-</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8b1ca31..8fad07a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -217,6 +217,7 @@
<java-symbol type="id" name="pin_confirm_text" />
<java-symbol type="id" name="pin_error_message" />
<java-symbol type="id" name="timePickerLayout" />
+ <java-symbol type="id" name="profile_icon" />
<java-symbol type="attr" name="actionModeShareDrawable" />
<java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -287,6 +288,8 @@
<java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
<java-symbol type="bool" name="config_windowIsRound" />
+ <java-symbol type="integer" name="config_bluetooth_max_advertisers" />
+ <java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_extraFreeKbytesAdjust" />
<java-symbol type="integer" name="config_extraFreeKbytesAbsolute" />
@@ -344,7 +347,7 @@
<java-symbol type="dimen" name="notification_title_text_size" />
<java-symbol type="dimen" name="notification_subtext_size" />
<java-symbol type="dimen" name="immersive_mode_cling_width" />
- <java-symbol type="dimen" name="notification_quantum_rounded_rect_radius" />
+ <java-symbol type="dimen" name="notification_material_rounded_rect_radius" />
<java-symbol type="string" name="add_account_button_label" />
<java-symbol type="string" name="addToDictionary" />
@@ -1015,8 +1018,14 @@
<java-symbol type="drawable" name="text_edit_side_paste_window" />
<java-symbol type="drawable" name="text_edit_paste_window" />
<java-symbol type="drawable" name="btn_check_off" />
- <java-symbol type="drawable" name="btn_code_lock_default_holo" />
- <java-symbol type="drawable" name="btn_code_lock_touched_holo" />
+ <java-symbol type="drawable" name="btn_code_lock_default_mtrl_alpha" />
+ <java-symbol type="drawable" name="btn_code_lock_touched_mtrl_alpha" />
+ <java-symbol type="drawable" name="indicator_code_lock_point_area_default_mtrl_alpha" />
+ <java-symbol type="drawable" name="indicator_code_lock_point_area_mtrl_alpha" />
+ <java-symbol type="drawable" name="indicator_code_lock_drag_direction_up_mtrl_alpha" />
+ <java-symbol type="color" name="lock_pattern_view_regular_color" />
+ <java-symbol type="color" name="lock_pattern_view_success_color" />
+ <java-symbol type="color" name="lock_pattern_view_error_color" />
<java-symbol type="drawable" name="clock_dial" />
<java-symbol type="drawable" name="clock_hand_hour" />
<java-symbol type="drawable" name="clock_hand_minute" />
@@ -1060,11 +1069,6 @@
<java-symbol type="drawable" name="ic_print" />
<java-symbol type="drawable" name="ic_print_error" />
<java-symbol type="drawable" name="ic_grayedout_printer" />
- <java-symbol type="drawable" name="indicator_code_lock_drag_direction_green_up" />
- <java-symbol type="drawable" name="indicator_code_lock_drag_direction_red_up" />
- <java-symbol type="drawable" name="indicator_code_lock_point_area_default_holo" />
- <java-symbol type="drawable" name="indicator_code_lock_point_area_green_holo" />
- <java-symbol type="drawable" name="indicator_code_lock_point_area_red_holo" />
<java-symbol type="drawable" name="jog_dial_arrow_long_left_green" />
<java-symbol type="drawable" name="jog_dial_arrow_long_right_red" />
<java-symbol type="drawable" name="jog_dial_arrow_short_left_and_right" />
@@ -1116,6 +1120,7 @@
<java-symbol type="drawable" name="cling_arrow_up" />
<java-symbol type="drawable" name="cling_bg" />
<java-symbol type="drawable" name="ic_corp_badge" />
+ <java-symbol type="drawable" name="ic_corp_icon_badge" />
<java-symbol type="layout" name="action_bar_home" />
<java-symbol type="layout" name="action_bar_title_item" />
@@ -1453,9 +1458,11 @@
<java-symbol type="array" name="config_defaultNotificationVibePattern" />
<java-symbol type="array" name="config_notificationFallbackVibePattern" />
<java-symbol type="array" name="config_onlySingleDcAllowed" />
+ <java-symbol type="bool" name="config_useAttentionLight" />
<java-symbol type="bool" name="config_animateScreenLights" />
<java-symbol type="bool" name="config_automatic_brightness_available" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
+ <java-symbol type="bool" name="config_enableHardwareFlpOverlay" />
<java-symbol type="bool" name="config_enableGeocoderOverlay" />
<java-symbol type="bool" name="config_enableGeofenceOverlay" />
<java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
@@ -1503,7 +1510,7 @@
<java-symbol type="integer" name="config_defaultNotificationLedOn" />
<java-symbol type="integer" name="config_deskDockKeepsScreenOn" />
<java-symbol type="integer" name="config_lightSensorWarmupTime" />
- <java-symbol type="integer" name="config_lowBatteryCloseWarningLevel" />
+ <java-symbol type="integer" name="config_lowBatteryCloseWarningBump" />
<java-symbol type="integer" name="config_lowBatteryWarningLevel" />
<java-symbol type="integer" name="config_networkPolicyDefaultWarning" />
<java-symbol type="integer" name="config_networkTransitionTimeout" />
@@ -1512,6 +1519,7 @@
<java-symbol type="integer" name="config_notificationsBatteryLedOn" />
<java-symbol type="integer" name="config_notificationsBatteryLowARGB" />
<java-symbol type="integer" name="config_notificationsBatteryMediumARGB" />
+ <java-symbol type="integer" name="config_notificationServiceArchiveSize" />
<java-symbol type="integer" name="config_radioScanningTimeout" />
<java-symbol type="integer" name="config_screenBrightnessSettingMinimum" />
<java-symbol type="integer" name="config_screenBrightnessSettingMaximum" />
@@ -1546,6 +1554,7 @@
<java-symbol type="string" name="chooser_wallpaper" />
<java-symbol type="string" name="config_datause_iface" />
<java-symbol type="string" name="config_fusedLocationProviderPackageName" />
+ <java-symbol type="string" name="config_hardwareFlpPackageName" />
<java-symbol type="string" name="config_geocoderProviderPackageName" />
<java-symbol type="string" name="config_geofenceProviderPackageName" />
<java-symbol type="string" name="config_networkLocationProviderPackageName" />
@@ -1641,6 +1650,7 @@
<java-symbol type="integer" name="config_maximumScreenDimDuration" />
<java-symbol type="fraction" name="config_maximumScreenDimRatio" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
+ <java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
<java-symbol type="layout" name="resolver_list" />
@@ -1651,27 +1661,26 @@
<java-symbol type="integer" name="config_maxResolverActivityColumns" />
<java-symbol type="array" name="config_notificationSignalExtractors" />
- <java-symbol type="layout" name="notification_quantum_action" />
- <java-symbol type="layout" name="notification_quantum_action_list" />
- <java-symbol type="layout" name="notification_quantum_action_tombstone" />
- <java-symbol type="layout" name="notification_template_quantum_base" />
- <java-symbol type="layout" name="notification_template_quantum_big_base" />
- <java-symbol type="layout" name="notification_template_quantum_big_picture" />
- <java-symbol type="layout" name="notification_template_quantum_big_text" />
- <java-symbol type="layout" name="notification_template_quantum_inbox" />
- <java-symbol type="layout" name="notification_template_quantum_media" />
- <java-symbol type="layout" name="notification_template_quantum_big_media" />
+ <java-symbol type="layout" name="notification_material_action" />
+ <java-symbol type="layout" name="notification_material_action_list" />
+ <java-symbol type="layout" name="notification_material_action_tombstone" />
+ <java-symbol type="layout" name="notification_template_material_base" />
+ <java-symbol type="layout" name="notification_template_material_big_base" />
+ <java-symbol type="layout" name="notification_template_material_big_picture" />
+ <java-symbol type="layout" name="notification_template_material_big_text" />
+ <java-symbol type="layout" name="notification_template_material_inbox" />
+ <java-symbol type="layout" name="notification_template_material_media" />
+ <java-symbol type="layout" name="notification_template_material_big_media" />
<java-symbol type="layout" name="notification_template_icon_group" />
- <java-symbol type="layout" name="notification_quantum_media_action" />
+ <java-symbol type="layout" name="notification_material_media_action" />
<java-symbol type="color" name="notification_action_legacy_color_filter" />
<java-symbol type="color" name="notification_icon_bg_color" />
<java-symbol type="drawable" name="notification_icon_legacy_bg" />
<java-symbol type="drawable" name="notification_icon_legacy_bg_inset" />
- <java-symbol type="drawable" name="notification_quantum_bg_dim" />
- <java-symbol type="drawable" name="notification_quantum_bg" />
- <java-symbol type="drawable" name="notification_quantum_media_progress" />
+ <java-symbol type="drawable" name="notification_material_bg_dim" />
+ <java-symbol type="drawable" name="notification_material_bg" />
+ <java-symbol type="drawable" name="notification_material_media_progress" />
<java-symbol type="color" name="notification_media_action_bg" />
- <java-symbol type="color" name="notification_media_info_bg" />
<java-symbol type="color" name="notification_media_progress" />
<java-symbol type="id" name="media_action_area" />
@@ -1856,13 +1865,9 @@
<java-symbol type="string" name="timepicker_numbers_radius_multiplier_normal" />
<java-symbol type="string" name="timepicker_transition_mid_radius_multiplier" />
<java-symbol type="string" name="timepicker_transition_end_radius_multiplier" />
- <java-symbol type="color" name="timepicker_default_text_color_holo_light" />
- <java-symbol type="color" name="timepicker_default_disabled_color_holo_light" />
- <java-symbol type="color" name="timepicker_default_ampm_unselected_background_color_holo_light" />
- <java-symbol type="color" name="timepicker_default_ampm_selected_background_color_holo_light" />
<java-symbol type="array" name="config_clockTickVibePattern" />
- <!-- From various Quantum changes -->
+ <!-- From various Material changes -->
<java-symbol type="attr" name="toolbarStyle" />
<java-symbol type="attr" name="titleTextAppearance" />
<java-symbol type="attr" name="subtitleTextAppearance" />
@@ -1871,5 +1876,12 @@
<java-symbol type="id" name="icon_frame" />
<java-symbol type="style" name="Animation.VolumePanel" />
<java-symbol type="transition" name="no_transition" />
+ <java-symbol type="color" name="timepicker_default_text_color_material" />
+ <java-symbol type="color" name="timepicker_default_disabled_color_material" />
+ <java-symbol type="color" name="timepicker_default_ampm_unselected_background_color_material" />
+ <java-symbol type="color" name="timepicker_default_ampm_selected_background_color_material" />
+ <java-symbol type="color" name="timepicker_default_selector_color_material" />
+ <java-symbol type="color" name="timepicker_default_numbers_background_color_material" />
+ <java-symbol type="style" name="TextAppearance.Material.TimePicker.TimeLabel" />
</resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 648660b..cb5cb0c 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -55,7 +55,6 @@
<item name="colorPrimaryDark">@color/legacy_primary_dark</item>
<item name="colorPrimary">@color/legacy_primary</item>
- <item name="colorPrimaryLight">@color/legacy_primary_light</item>
<item name="colorControlActivated">@color/legacy_control_activated</item>
<item name="colorControlNormal">@color/legacy_control_normal</item>
<item name="colorControlHighlight">@color/legacy_button_pressed</item>
@@ -454,7 +453,6 @@
<item name="colorPrimaryDark">@color/legacy_light_primary_dark</item>
<item name="colorPrimary">@color/legacy_light_primary</item>
- <item name="colorPrimaryLight">@color/legacy_light_primary_light</item>
<item name="colorControlActivated">@color/legacy_light_control_activated</item>
<item name="colorControlNormal">@color/legacy_light_control_normal</item>
<item name="colorControlHighlight">@color/legacy_light_button_pressed</item>
@@ -969,7 +967,6 @@
<item name="colorPrimaryDark">@color/holo_primary_dark</item>
<item name="colorPrimary">@color/holo_primary</item>
- <item name="colorPrimaryLight">@color/holo_primary_light</item>
<item name="colorControlActivated">@color/holo_control_activated</item>
<item name="colorControlNormal">@color/holo_control_normal</item>
<item name="colorControlHighlight">@color/holo_button_pressed</item>
@@ -1310,7 +1307,6 @@
<item name="colorPrimaryDark">@color/holo_light_primary_dark</item>
<item name="colorPrimary">@color/holo_light_primary</item>
- <item name="colorPrimaryLight">@color/holo_light_primary_light</item>
<item name="colorControlActivated">@color/holo_light_control_activated</item>
<item name="colorControlNormal">@color/holo_light_control_normal</item>
<item name="colorControlHighlight">@color/holo_light_button_pressed</item>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index aee35ef..27c8754 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -38,18 +38,18 @@
to a device’s native theme with all device customizations intact.</p>
<p>For example, when you set your app's {@code targetSdkVersion} to XX or higher, this
theme is applied to your application by default. As such, your app might appear with the
- {@link #Theme_Quantum Quantum} styles on one device, but with a different set of styles on
+ {@link #Theme_Material Material} styles on one device, but with a different set of styles on
another device. This is great if you want your app to fit with the device's native look and
feel. If, however, you prefer to keep your UI style the same across all devices, you should
- apply a specific theme such as {@link #Theme_Quantum Quantum} or one of your own design.
+ apply a specific theme such as {@link #Theme_Material Material} or one of your own design.
For more information, read <a
- href="http://android-developers.blogspot.com/20XX/XX/quantum-everywhere.html">Quantum
+ href="http://android-developers.blogspot.com/20XX/XX/material-everywhere.html">Material
Everywhere</a>.</p>
<p>Styles used by the DeviceDefault theme are named using the convention
Type.DeviceDefault.Etc (for example, {@code Widget.DeviceDefault.Button} and
{@code TextAppearance.DeviceDefault.Widget.PopupMenu.Large}).</p>
-->
- <style name="Theme.DeviceDefault" parent="Theme.Quantum" >
+ <style name="Theme.DeviceDefault" parent="Theme.Material" >
<!-- Text styles -->
<item name="textAppearance">@style/TextAppearance.DeviceDefault</item>
<item name="textAppearanceInverse">@style/TextAppearance.DeviceDefault.Inverse</item>
@@ -210,27 +210,27 @@
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
- <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Quantum.NoActionBar" />
+ <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Material.NoActionBar" />
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Quantum.NoActionBar.Fullscreen" />
+ <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen" />
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
extending in to overscan region. This theme
sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Quantum.NoActionBar.Overscan" />
+ <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan" />
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
{@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Quantum.NoActionBar.TranslucentDecor" />
+ <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor" />
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
floating (not fill the entire screen), and puts a frame around its contents. You can set this
theme on an activity if you would like to make an activity that looks like a Dialog. -->
- <style name="Theme.DeviceDefault.Dialog" parent="Theme.Quantum.Dialog" >
+ <style name="Theme.DeviceDefault.Dialog" parent="Theme.Material.Dialog" >
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
<item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
@@ -243,14 +243,14 @@
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
regular dialog. -->
- <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Quantum.Dialog.MinWidth" />
+ <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth" />
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
- <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Quantum.Dialog.NoActionBar" />
+ <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar" />
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
for a regular dialog. -->
- <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Quantum.Dialog.NoActionBar.MinWidth" />
+ <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth" />
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
<style name="Theme.DeviceDefault.Dialog.FixedSize">
@@ -270,49 +270,49 @@
<!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
- <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Quantum.DialogWhenLarge" />
+ <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge" />
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
xlarge). -->
- <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Quantum.DialogWhenLarge.NoActionBar" />
+ <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar" />
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
- <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Quantum.Dialog.Presentation" />
+ <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation" />
- <style name="Theme.DeviceDefault.Dialog.TimePicker" parent="Theme.Quantum.Dialog.TimePicker"/>
+ <style name="Theme.DeviceDefault.Dialog.TimePicker" parent="Theme.Material.Dialog.TimePicker"/>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
- <style name="Theme.DeviceDefault.Panel" parent="Theme.Quantum.Panel" />
+ <style name="Theme.DeviceDefault.Panel" parent="Theme.Material.Panel" />
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
behind them. -->
- <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Quantum.Wallpaper" />
+ <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Material.Wallpaper" />
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
behind them and without an action bar. -->
- <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Quantum.Wallpaper.NoTitleBar" />
+ <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Material.Wallpaper.NoTitleBar" />
<!-- DeviceDefault style for input methods, which is used by the
{@link android.inputmethodservice.InputMethodService} class.-->
- <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Quantum.InputMethod" />
+ <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Material.InputMethod" />
<!-- DeviceDefault style for input methods, which is used by the
{@link android.service.voice.VoiceInteractionSession} class.-->
- <style name="Theme.DeviceDefault.VoiceInteractionSession" parent="Theme.Quantum.VoiceInteractionSession" >
+ <style name="Theme.DeviceDefault.VoiceInteractionSession" parent="Theme.Material.VoiceInteractionSession" >
</style>
- <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Quantum.Dialog.Alert">
+ <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
</style>
- <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Quantum.SearchBar" />
- <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Quantum.Dialog.NoFrame" />
+ <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar" />
+ <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame" />
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
- <style name="Theme.DeviceDefault.Light" parent="Theme.Quantum.Light" >
+ <style name="Theme.DeviceDefault.Light" parent="Theme.Material.Light" >
<!-- Text styles -->
<item name="textAppearance">@style/TextAppearance.DeviceDefault</item>
<item name="textAppearanceInverse">@style/TextAppearance.DeviceDefault.Inverse</item>
@@ -469,30 +469,30 @@
<!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
inverse color profile. -->
- <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Quantum.Light.DarkActionBar" />
+ <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Material.Light.DarkActionBar" />
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
- <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Quantum.Light.NoActionBar" />
+ <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Material.Light.NoActionBar" />
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
This theme sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Quantum.Light.NoActionBar.Fullscreen" />
+ <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen" />
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
and extending in to overscan region. This theme
sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Quantum.Light.NoActionBar.Overscan" />
+ <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan" />
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
{@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Quantum.Light.NoActionBar.TranslucentDecor" />
+ <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor" />
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
floating (not fill the entire screen), and puts a frame around its contents. You can set this
theme on an activity if you would like to make an activity that looks like a Dialog.-->
- <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Quantum.Light.Dialog" >
+ <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Material.Light.Dialog" >
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
<item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
@@ -505,14 +505,14 @@
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
regular dialog. -->
- <style name="Theme.DeviceDefault.Light.Dialog.MinWidth" parent="Theme.Quantum.Light.Dialog.MinWidth" />
+ <style name="Theme.DeviceDefault.Light.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth" />
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
- <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Quantum.Light.Dialog.NoActionBar" />
+ <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar" />
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
width for a regular dialog. -->
- <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Quantum.Light.Dialog.NoActionBar.MinWidth" />
+ <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth" />
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
<style name="Theme.DeviceDefault.Light.Dialog.FixedSize">
@@ -532,27 +532,30 @@
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Quantum.Light.DialogWhenLarge" />
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge" />
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
xlarge). -->
- <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Quantum.Light.DialogWhenLarge.NoActionBar" />
+ <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar" />
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
- <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Quantum.Light.Dialog.Presentation" />
+ <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation" />
- <style name="Theme.DeviceDefault.Light.Dialog.TimePicker" parent="Theme.Quantum.Light.Dialog.TimePicker"/>
+ <style name="Theme.DeviceDefault.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.TimePicker"/>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
decorations, so you basically have an empty rectangle in which to place your content. It makes
the window floating, with a transparent background, and turns off dimming behind the window. -->
- <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Quantum.Light.Panel" />
+ <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Material.Light.Panel" />
- <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Quantum.Light.Dialog.Alert">
+ <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
</style>
- <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Quantum.Light.SearchBar" />
+ <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar" />
+
+ <!-- DeviceDefault theme for a window that should look like the Settings app. -->
+ <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings" />
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
new file mode 100644
index 0000000..97ad7e4
--- /dev/null
+++ b/core/res/res/values/themes_material.xml
@@ -0,0 +1,1242 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+===============================================================
+ PLEASE READ
+===============================================================
+
+The Material themes must not be modified in order to pass CTS.
+Many related themes and styles depend on other values defined in this file.
+If you would like to provide custom themes and styles for your device,
+please see themes_device_defaults.xml.
+
+===============================================================
+ PLEASE READ
+===============================================================
+ -->
+<resources>
+
+ <!-- Material theme (dark version).
+ <p>If you want to ensure that your
+ app consistently uses the Material theme at all times, you must explicitly declare it in your
+ manifest. For example, {@code <application android:theme="@style/Theme.Material">}.
+
+ <p>Styles used by the Material theme are named using the convention Type.Material.Etc
+ (for example, {@code Widget.Material.Button} and {@code
+ TextAppearance.Material.Widget.PopupMenu.Large}).
+ Specific resources used by Material are named using the convention @type/foo_bar_baz_material
+ with trailing _dark or _light specifiers if they are not shared between both light and
+ dark versions of the theme. -->
+ <style name="Theme.Material">
+ <item name="colorForeground">@color/bright_foreground_material_dark</item>
+ <item name="colorForegroundInverse">@color/bright_foreground_material_light</item>
+ <item name="colorBackground">@color/background_material_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
+ <item name="disabledAlpha">0.5</item>
+ <item name="backgroundDimAmount">0.6</item>
+
+ <!-- Text styles -->
+ <item name="textAppearance">@style/TextAppearance.Material</item>
+ <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
+
+ <item name="textColorPrimary">@color/primary_text_material_dark</item>
+ <item name="textColorPrimaryInverse">@color/primary_text_material_light</item>
+ <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
+ <item name="textColorSecondary">@color/secondary_text_material_dark</item>
+ <item name="textColorSecondaryInverse">@color/secondary_text_material_light</item>
+ <item name="textColorTertiary">@color/tertiary_text_material_dark</item>
+ <item name="textColorTertiaryInverse">@color/tertiary_text_material_light</item>
+ <item name="textColorHint">@color/hint_foreground_material_dark</item>
+ <item name="textColorHintInverse">@color/hint_foreground_material_light</item>
+ <item name="textColorHighlight">@color/highlighted_text_material_dark</item>
+ <item name="textColorHighlightInverse">@color/highlighted_text_material_light</item>
+ <item name="textColorLink">@color/material_teal_500</item>
+ <item name="textColorLinkInverse">@color/material_teal_500</item>
+ <item name="textColorSearchUrl">@color/search_url_text_material_dark</item>
+ <item name="textColorAlertDialogListItem">@color/primary_text_material_dark</item>
+
+ <item name="textAppearanceLarge">@style/TextAppearance.Material.Large</item>
+ <item name="textAppearanceLargeInverse">@style/TextAppearance.Material.Large.Inverse</item>
+ <item name="textAppearanceMedium">@style/TextAppearance.Material.Medium</item>
+ <item name="textAppearanceMediumInverse">@style/TextAppearance.Material.Medium.Inverse</item>
+ <item name="textAppearanceSmall">@style/TextAppearance.Material.Small</item>
+ <item name="textAppearanceSmallInverse">@style/TextAppearance.Material.Small.Inverse</item>
+ <item name="textAppearanceSearchResultTitle">@style/TextAppearance.Material.SearchResult.Title</item>
+ <item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.Material.SearchResult.Subtitle</item>
+
+ <item name="textAppearanceButton">@style/TextAppearance.Material.Widget.Button</item>
+
+ <item name="editTextColor">?attr/textColorPrimary</item>
+ <item name="editTextBackground">@drawable/edit_text_material</item>
+
+ <item name="candidatesTextStyleSpans">@string/candidates_style</item>
+
+ <item name="textCheckMark">@drawable/indicator_check_mark_dark</item>
+ <item name="textCheckMarkInverse">@drawable/indicator_check_mark_light</item>
+
+ <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Large</item>
+ <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Small</item>
+
+ <!-- Button styles -->
+ <item name="buttonStyle">@style/Widget.Material.Button</item>
+ <item name="buttonStyleSmall">@style/Widget.Material.Button.Small</item>
+ <item name="buttonStyleInset">@style/Widget.Material.Button.Inset</item>
+ <item name="buttonStyleToggle">@style/Widget.Material.Button.Toggle</item>
+
+ <item name="switchStyle">@style/Widget.Material.CompoundButton.Switch</item>
+ <item name="mediaRouteButtonStyle">@style/Widget.Material.MediaRouteButton</item>
+
+ <item name="selectableItemBackground">@drawable/item_background_material</item>
+ <item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_material</item>
+ <item name="borderlessButtonStyle">@style/Widget.Material.Button.Borderless</item>
+ <item name="homeAsUpIndicator">@drawable/ic_ab_back_material</item>
+
+ <!-- List attributes -->
+ <item name="listPreferredItemHeight">64dip</item>
+ <item name="listPreferredItemHeightSmall">48dip</item>
+ <item name="listPreferredItemHeightLarge">80dip</item>
+ <item name="dropdownListPreferredItemHeight">?attr/listPreferredItemHeightSmall</item>
+ <item name="textAppearanceListItem">@style/TextAppearance.Material.Subhead</item>
+ <item name="textAppearanceListItemSmall">@style/TextAppearance.Material.Subhead</item>
+ <item name="textAppearanceListItemSecondary">@style/TextAppearance.Material.Body1</item>
+ <item name="listPreferredItemPaddingLeft">16dip</item>
+ <item name="listPreferredItemPaddingRight">16dip</item>
+ <item name="listPreferredItemPaddingStart">16dip</item>
+ <item name="listPreferredItemPaddingEnd">16dip</item>
+
+ <!-- @hide -->
+ <item name="searchResultListItemHeight">58dip</item>
+ <item name="listDivider">@drawable/list_divider_material</item>
+ <item name="listSeparatorTextViewStyle">@style/Widget.Material.TextView.ListSeparator</item>
+
+ <item name="listChoiceIndicatorSingle">@drawable/btn_radio_material_anim</item>
+ <item name="listChoiceIndicatorMultiple">@drawable/btn_check_material_anim</item>
+
+ <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
+ <item name="activatedBackgroundIndicator">@drawable/activated_background_material</item>
+
+ <item name="listDividerAlertDialog">@drawable/list_divider_material</item>
+
+ <item name="expandableListPreferredItemPaddingLeft">40dip</item>
+ <item name="expandableListPreferredChildPaddingLeft">?attr/expandableListPreferredItemPaddingLeft</item>
+
+ <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
+ <item name="expandableListPreferredItemIndicatorRight">0dip</item>
+ <item name="expandableListPreferredChildIndicatorLeft">?attr/expandableListPreferredItemIndicatorLeft</item>
+ <item name="expandableListPreferredChildIndicatorRight">?attr/expandableListPreferredItemIndicatorRight</item>
+ <item name="findOnPageNextDrawable">@drawable/ic_find_next_material</item>
+ <item name="findOnPagePreviousDrawable">@drawable/ic_find_previous_material</item>
+
+ <!-- Gallery attributes -->
+ <item name="galleryItemBackground">@drawable/gallery_item_background</item>
+
+ <!-- Window attributes -->
+ <item name="windowBackground">@color/background_material_dark</item>
+ <item name="windowFrame">@null</item>
+ <item name="windowNoTitle">false</item>
+ <item name="windowFullscreen">false</item>
+ <item name="windowOverscan">false</item>
+ <item name="windowIsFloating">false</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="windowShowWallpaper">false</item>
+ <item name="windowTitleStyle">@style/WindowTitle.Material</item>
+ <item name="windowTitleSize">@dimen/action_bar_default_height_material</item>
+ <item name="windowTitleBackgroundStyle">@style/WindowTitleBackground.Material</item>
+ <item name="windowContentTransitions">false</item>
+ <item name="windowAnimationStyle">@style/Animation.Material.Activity</item>
+ <item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+ <item name="windowActionBar">true</item>
+ <item name="windowActionModeOverlay">false</item>
+ <item name="windowDrawsSystemBarBackgrounds">true</item>
+ <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
+ <item name="statusBarColor">?attr/colorPrimaryDark</item>
+ <item name="navigationBarColor">@color/black</item>
+ <item name="windowEnterTransition">@transition/fade</item>
+ <item name="windowSharedElementEnterTransition">@transition/move</item>
+ <item name="windowSharedElementExitTransition">@transition/move</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogTheme">@style/Theme.Material.Dialog</item>
+ <item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_material</item>
+ <item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_material</item>
+ <item name="dialogTitleDecorLayout">@layout/dialog_title_material</item>
+
+ <!-- AlertDialog attributes -->
+ <item name="alertDialogTheme">@style/Theme.Material.Dialog.Alert</item>
+ <item name="alertDialogStyle">@style/AlertDialog.Material</item>
+ <item name="alertDialogCenterButtons">false</item>
+ <item name="alertDialogIcon">@drawable/ic_dialog_alert_material</item>
+
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@style/Theme.Material.Dialog.Presentation</item>
+
+ <!-- Toast attributes -->
+ <item name="toastFrameBackground">@drawable/toast_frame</item>
+
+ <!-- Panel attributes -->
+ <item name="panelBackground">?attr/colorBackground</item>
+ <item name="panelFullBackground">@drawable/menu_background_fill_parent_width</item>
+ <!-- These three attributes do not seems to be used by the framework. Declared public though -->
+ <item name="panelColorBackground">#000</item>
+ <item name="panelColorForeground">?attr/textColorPrimary</item>
+ <item name="panelTextAppearance">?attr/textAppearance</item>
+
+ <item name="panelMenuIsCompact">true</item>
+ <item name="panelMenuListWidth">250dip</item>
+ <item name="panelMenuListTheme">@style/Theme.Material.CompactMenu</item>
+
+ <!-- Scrollbar attributes -->
+ <item name="scrollbarFadeDuration">250</item>
+ <item name="scrollbarDefaultDelayBeforeFade">300</item>
+ <item name="scrollbarSize">10dip</item>
+ <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
+ <item name="scrollbarThumbVertical">@drawable/scrollbar_handle_material</item>
+ <item name="scrollbarTrackHorizontal">@null</item>
+ <item name="scrollbarTrackVertical">@null</item>
+
+ <!-- Text selection handle attributes -->
+ <item name="textSelectHandleLeft">@drawable/text_select_handle_left_material</item>
+ <item name="textSelectHandleRight">@drawable/text_select_handle_right_material</item>
+ <item name="textSelectHandle">@drawable/text_select_handle_middle_material</item>
+ <item name="textSelectHandleWindowStyle">@style/Widget.Material.TextSelectHandle</item>
+ <item name="textSuggestionsWindowStyle">@style/Widget.Material.TextSuggestionsPopupWindow</item>
+ <item name="textCursorDrawable">@drawable/text_cursor_material</item>
+
+ <!-- Widget styles -->
+ <item name="absListViewStyle">@style/Widget.Material.AbsListView</item>
+ <item name="autoCompleteTextViewStyle">@style/Widget.Material.AutoCompleteTextView</item>
+ <item name="checkboxStyle">@style/Widget.Material.CompoundButton.CheckBox</item>
+ <item name="checkedTextViewStyle">@style/Widget.Material.CheckedTextView</item>
+ <item name="dropDownListViewStyle">@style/Widget.Material.ListView.DropDown</item>
+ <item name="editTextStyle">@style/Widget.Material.EditText</item>
+ <item name="expandableListViewStyle">@style/Widget.Material.ExpandableListView</item>
+ <item name="expandableListViewWhiteStyle">@style/Widget.Material.ExpandableListView.White</item>
+ <item name="fastScrollStyle">@style/Widget.Material.FastScroll</item>
+ <item name="galleryStyle">@style/Widget.Material.Gallery</item>
+ <item name="gestureOverlayViewStyle">@style/Widget.Material.GestureOverlayView</item>
+ <item name="gridViewStyle">@style/Widget.Material.GridView</item>
+ <item name="imageButtonStyle">@style/Widget.Material.ImageButton</item>
+ <item name="imageWellStyle">@style/Widget.Material.ImageWell</item>
+ <item name="listViewStyle">@style/Widget.Material.ListView</item>
+ <item name="listViewWhiteStyle">@style/Widget.Material.ListView.White</item>
+ <item name="popupWindowStyle">@style/Widget.Material.PopupWindow</item>
+ <item name="progressBarStyle">@style/Widget.Material.ProgressBar</item>
+ <item name="progressBarStyleHorizontal">@style/Widget.Material.ProgressBar.Horizontal</item>
+ <item name="progressBarStyleSmall">@style/Widget.Material.ProgressBar.Small</item>
+ <item name="progressBarStyleSmallTitle">@style/Widget.Material.ProgressBar.Small.Title</item>
+ <item name="progressBarStyleLarge">@style/Widget.Material.ProgressBar.Large</item>
+ <item name="progressBarStyleInverse">@style/Widget.Material.ProgressBar.Inverse</item>
+ <item name="progressBarStyleSmallInverse">@style/Widget.Material.ProgressBar.Small.Inverse</item>
+ <item name="progressBarStyleLargeInverse">@style/Widget.Material.ProgressBar.Large.Inverse</item>
+ <item name="seekBarStyle">@style/Widget.Material.SeekBar</item>
+ <item name="ratingBarStyle">@style/Widget.Material.RatingBar</item>
+ <item name="ratingBarStyleIndicator">@style/Widget.Material.RatingBar.Indicator</item>
+ <item name="ratingBarStyleSmall">@style/Widget.Material.RatingBar.Small</item>
+ <item name="radioButtonStyle">@style/Widget.Material.CompoundButton.RadioButton</item>
+ <item name="scrollViewStyle">@style/Widget.Material.ScrollView</item>
+ <item name="horizontalScrollViewStyle">@style/Widget.Material.HorizontalScrollView</item>
+ <item name="spinnerStyle">?attr/dropDownSpinnerStyle</item>
+ <item name="dropDownSpinnerStyle">@style/Widget.Material.Spinner.DropDown</item>
+ <item name="starStyle">@style/Widget.Material.CompoundButton.Star</item>
+ <item name="tabWidgetStyle">@style/Widget.Material.TabWidget</item>
+ <item name="textViewStyle">@style/Widget.Material.TextView</item>
+ <item name="errorMessageBackground">@drawable/popup_inline_error_holo_dark</item>
+ <item name="errorMessageAboveBackground">@drawable/popup_inline_error_above_holo_dark</item>
+ <item name="webTextViewStyle">@style/Widget.Material.WebTextView</item>
+ <item name="webViewStyle">@style/Widget.Material.WebView</item>
+ <item name="dropDownItemStyle">@style/Widget.Material.DropDownItem</item>
+ <item name="spinnerDropDownItemStyle">@style/Widget.Material.DropDownItem.Spinner</item>
+ <item name="spinnerItemStyle">@style/Widget.Material.TextView.SpinnerItem</item>
+ <item name="dropDownHintAppearance">@style/TextAppearance.Material.Widget.DropDownHint</item>
+ <item name="keyboardViewStyle">@style/Widget.Material.KeyboardView</item>
+ <item name="quickContactBadgeStyleWindowSmall">@style/Widget.Material.QuickContactBadge.WindowSmall</item>
+ <item name="quickContactBadgeStyleWindowMedium">@style/Widget.Material.QuickContactBadge.WindowMedium</item>
+ <item name="quickContactBadgeStyleWindowLarge">@style/Widget.Material.QuickContactBadge.WindowLarge</item>
+ <item name="quickContactBadgeStyleSmallWindowSmall">@style/Widget.Material.QuickContactBadgeSmall.WindowSmall</item>
+ <item name="quickContactBadgeStyleSmallWindowMedium">@style/Widget.Material.QuickContactBadgeSmall.WindowMedium</item>
+ <item name="quickContactBadgeStyleSmallWindowLarge">@style/Widget.Material.QuickContactBadgeSmall.WindowLarge</item>
+ <item name="listPopupWindowStyle">@style/Widget.Material.ListPopupWindow</item>
+ <item name="popupMenuStyle">@style/Widget.Material.PopupMenu</item>
+ <item name="stackViewStyle">@style/Widget.Material.StackView</item>
+ <item name="activityChooserViewStyle">@style/Widget.Material.ActivityChooserView</item>
+
+ <!-- Preference styles -->
+ <item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
+ <item name="preferenceFragmentStyle">@style/PreferenceFragment.Material</item>
+ <item name="preferenceFragmentPaddingSide">0dip</item>
+ <item name="preferenceCategoryStyle">@style/Preference.Material.Category</item>
+ <item name="preferenceStyle">@style/Preference.Material</item>
+ <item name="preferenceInformationStyle">@style/Preference.Material.Information</item>
+ <item name="checkBoxPreferenceStyle">@style/Preference.Material.CheckBoxPreference</item>
+ <item name="switchPreferenceStyle">@style/Preference.Material.SwitchPreference</item>
+ <item name="yesNoPreferenceStyle">@style/Preference.Material.DialogPreference.YesNoPreference</item>
+ <item name="dialogPreferenceStyle">@style/Preference.Material.DialogPreference</item>
+ <item name="editTextPreferenceStyle">@style/Preference.Material.DialogPreference.EditTextPreference</item>
+ <item name="ringtonePreferenceStyle">@style/Preference.Material.RingtonePreference</item>
+ <item name="preferenceLayoutChild">@layout/preference_child_material</item>
+ <item name="detailsElementBackground">?attr/colorBackground</item>
+
+ <!-- Search widget styles -->
+ <item name="searchWidgetCorpusItemBackground">@color/search_widget_corpus_item_background</item>
+
+ <!-- Action bar styles -->
+ <item name="actionDropDownStyle">@style/Widget.Material.Spinner.DropDown.ActionBar</item>
+ <item name="actionButtonStyle">@style/Widget.Material.ActionButton</item>
+ <item name="actionOverflowButtonStyle">@style/Widget.Material.ActionButton.Overflow</item>
+ <item name="actionOverflowMenuStyle">@android:style/Widget.Material.PopupMenu.Overflow</item>
+ <item name="actionModeBackground">?attr/colorPrimaryDark</item>
+ <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
+ <item name="actionModeCloseDrawable">@drawable/ic_cab_done_material</item>
+ <item name="actionBarTabStyle">@style/Widget.Material.ActionBar.TabView</item>
+ <item name="actionBarTabBarStyle">@style/Widget.Material.ActionBar.TabBar</item>
+ <item name="actionBarTabTextStyle">@style/Widget.Material.ActionBar.TabText</item>
+ <item name="actionModeStyle">@style/Widget.Material.ActionMode</item>
+ <item name="actionModeCloseButtonStyle">@style/Widget.Material.ActionButton.CloseMode</item>
+ <item name="actionBarStyle">@style/Widget.Material.ActionBar.Solid</item>
+ <item name="actionBarSize">@dimen/action_bar_default_height_material</item>
+ <item name="actionModePopupWindowStyle">@style/Widget.Material.PopupWindow.ActionMode</item>
+ <item name="actionBarWidgetTheme">@null</item>
+ <item name="actionBarTheme">@style/ThemeOverlay.Material.ActionBar</item>
+ <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
+
+ <item name="actionModeCutDrawable">@drawable/ic_menu_cut_material</item>
+ <item name="actionModeCopyDrawable">@drawable/ic_menu_copy_material</item>
+ <item name="actionModePasteDrawable">@drawable/ic_menu_paste_material</item>
+ <item name="actionModeSelectAllDrawable">@drawable/ic_menu_selectall_material</item>
+ <item name="actionModeShareDrawable">@drawable/ic_menu_share_material</item>
+ <item name="actionModeFindDrawable">@drawable/ic_menu_find_material</item>
+ <item name="actionModeWebSearchDrawable">@drawable/ic_menu_search_material</item>
+
+ <item name="toolbarStyle">@style/Widget.Material.Toolbar</item>
+
+ <item name="dividerVertical">?attr/listDivider</item>
+ <item name="dividerHorizontal">?attr/listDivider</item>
+ <item name="buttonBarStyle">@style/Widget.Material.ButtonBar</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Material.Button.Borderless</item>
+ <item name="segmentedButtonStyle">@style/Widget.Material.SegmentedButton</item>
+
+ <!-- SearchView attributes -->
+ <item name="searchDropdownBackground">?attr/colorBackground</item>
+ <item name="searchViewTextField">@drawable/textfield_search_material</item>
+ <item name="searchViewTextFieldRight">@drawable/textfield_search_material</item>
+ <item name="searchViewCloseIcon">@android:drawable/ic_clear_material</item>
+ <item name="searchViewSearchIcon">@android:drawable/ic_search_api_material</item>
+ <item name="searchViewGoIcon">@android:drawable/ic_go_search_api_material</item>
+ <item name="searchViewVoiceIcon">@android:drawable/ic_voice_search_api_material</item>
+ <item name="searchViewEditQuery">@android:drawable/ic_commit_search_api_material</item>
+
+ <item name="searchDialogTheme">@style/Theme.Material.SearchBar</item>
+
+ <!-- PreferenceFrameLayout attributes -->
+ <item name="preferenceFrameLayoutStyle">@style/Widget.Material.PreferenceFrameLayout</item>
+
+ <!-- NumberPicker style-->
+ <item name="numberPickerStyle">@style/Widget.Material.NumberPicker</item>
+
+ <!-- CalendarView style-->
+ <item name="calendarViewStyle">@style/Widget.Material.CalendarView</item>
+
+ <!-- TimePicker style -->
+ <item name="timePickerStyle">@style/Widget.Material.TimePicker</item>
+
+ <!-- TimePicker background color -->
+ <item name="timePickerHeaderBackgroundColor">?colorBackground</item>
+
+ <!-- TimePicker Header time label text appearance -->
+ <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
+
+ <!-- TimePicker Header am pm label text appearance -->
+ <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
+
+ <!-- TimePicker dialog theme -->
+ <item name="timePickerDialogTheme">@style/Theme.Material.Dialog.TimePicker</item>
+
+ <!-- DatePicker style -->
+ <item name="datePickerStyle">@style/Widget.Material.DatePicker</item>
+
+ <!-- TODO: This belongs in a FastScroll style -->
+ <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_material</item>
+ <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
+ <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
+ <item name="fastScrollTrackDrawable">@drawable/fastscroll_track_material</item>
+ <item name="fastScrollOverlayPosition">atThumb</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimaryDark">@color/material_blue_grey_900</item>
+ <item name="colorPrimary">@color/material_blue_grey_800</item>
+ <item name="colorAccent">@color/material_light_blue_A200</item>
+
+ <item name="colorControlNormal">?attr/textColorSecondary</item>
+ <item name="colorControlActivated">?attr/colorAccent</item>
+
+ <item name="colorControlHighlight">@color/ripple_material_dark</item>
+ <item name="colorButtonNormal">@color/btn_default_material_dark</item>
+ </style>
+
+ <!-- Material theme (light version). -->
+ <style name="Theme.Material.Light" parent="Theme.Light">
+ <item name="colorForeground">@color/bright_foreground_material_light</item>
+ <item name="colorForegroundInverse">@color/bright_foreground_material_dark</item>
+ <item name="colorBackground">@color/background_material_light</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
+ <item name="disabledAlpha">0.5</item>
+ <item name="backgroundDimAmount">0.6</item>
+
+ <!-- Text styles -->
+ <item name="textAppearance">@style/TextAppearance.Material</item>
+ <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
+
+ <item name="textColorPrimary">@color/primary_text_material_light</item>
+ <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
+ <item name="textColorSecondary">@color/secondary_text_material_light</item>
+ <item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item>
+ <item name="textColorTertiary">@color/tertiary_text_material_light</item>
+ <item name="textColorTertiaryInverse">@color/tertiary_text_material_dark</item>
+ <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_light</item>
+ <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_material_dark</item>
+ <item name="textColorHint">@color/hint_foreground_material_light</item>
+ <item name="textColorHintInverse">@color/hint_foreground_material_dark</item>
+ <item name="textColorHighlight">@color/highlighted_text_material_light</item>
+ <item name="textColorHighlightInverse">@color/highlighted_text_material_dark</item>
+ <item name="textColorLink">@color/material_teal_500</item>
+ <item name="textColorLinkInverse">@color/material_teal_500</item>
+ <item name="textColorSearchUrl">@color/search_url_text_material_light</item>
+ <item name="textColorAlertDialogListItem">@color/primary_text_material_light</item>
+
+ <item name="textAppearanceLarge">@style/TextAppearance.Material.Large</item>
+ <item name="textAppearanceLargeInverse">@style/TextAppearance.Material.Large.Inverse</item>
+ <item name="textAppearanceMedium">@style/TextAppearance.Material.Medium</item>
+ <item name="textAppearanceMediumInverse">@style/TextAppearance.Material.Medium.Inverse</item>
+ <item name="textAppearanceSmall">@style/TextAppearance.Material.Small</item>
+ <item name="textAppearanceSmallInverse">@style/TextAppearance.Material.Small.Inverse</item>
+ <item name="textAppearanceSearchResultTitle">@style/TextAppearance.Material.SearchResult.Title</item>
+ <item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.Material.SearchResult.Subtitle</item>
+
+ <item name="textAppearanceButton">@style/TextAppearance.Material.Widget.Button</item>
+
+ <item name="editTextColor">?attr/textColorPrimary</item>
+ <item name="editTextBackground">@drawable/edit_text_material</item>
+
+ <item name="candidatesTextStyleSpans">@string/candidates_style</item>
+
+ <item name="textCheckMark">@drawable/indicator_check_mark_light</item>
+ <item name="textCheckMarkInverse">@drawable/indicator_check_mark_dark</item>
+
+ <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Large</item>
+ <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Material.Widget.PopupMenu.Small</item>
+
+ <!-- Button styles -->
+ <item name="buttonStyle">@style/Widget.Material.Light.Button</item>
+
+ <item name="buttonStyleSmall">@style/Widget.Material.Light.Button.Small</item>
+ <item name="buttonStyleInset">@style/Widget.Material.Light.Button.Inset</item>
+
+ <item name="buttonStyleToggle">@style/Widget.Material.Light.Button.Toggle</item>
+ <item name="switchStyle">@style/Widget.Material.Light.CompoundButton.Switch</item>
+ <item name="mediaRouteButtonStyle">@style/Widget.Material.Light.MediaRouteButton</item>
+
+ <item name="selectableItemBackground">@drawable/item_background_material</item>
+ <item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_material</item>
+ <item name="borderlessButtonStyle">@style/Widget.Material.Light.Button.Borderless</item>
+ <item name="homeAsUpIndicator">@drawable/ic_ab_back_material</item>
+
+ <!-- List attributes -->
+ <item name="listPreferredItemHeight">64dip</item>
+ <item name="listPreferredItemHeightSmall">48dip</item>
+ <item name="listPreferredItemHeightLarge">80dip</item>
+ <item name="dropdownListPreferredItemHeight">?attr/listPreferredItemHeightSmall</item>
+ <item name="textAppearanceListItem">@style/TextAppearance.Material.Subhead</item>
+ <item name="textAppearanceListItemSmall">@style/TextAppearance.Material.Subhead</item>
+ <item name="textAppearanceListItemSecondary">@style/TextAppearance.Material.Body1</item>
+ <item name="listPreferredItemPaddingLeft">16dip</item>
+ <item name="listPreferredItemPaddingRight">16dip</item>
+ <item name="listPreferredItemPaddingStart">16dip</item>
+ <item name="listPreferredItemPaddingEnd">16dip</item>
+
+ <!-- @hide -->
+ <item name="searchResultListItemHeight">58dip</item>
+ <item name="listDivider">@drawable/list_divider_material</item>
+ <item name="listSeparatorTextViewStyle">@style/Widget.Material.Light.TextView.ListSeparator</item>
+
+ <item name="listChoiceIndicatorSingle">@drawable/btn_radio_material_anim</item>
+ <item name="listChoiceIndicatorMultiple">@drawable/btn_check_material_anim</item>
+
+ <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
+ <item name="activatedBackgroundIndicator">@drawable/activated_background_material</item>
+
+ <item name="expandableListPreferredItemPaddingLeft">40dip</item>
+ <item name="expandableListPreferredChildPaddingLeft">?attr/expandableListPreferredItemPaddingLeft</item>
+
+ <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
+ <item name="expandableListPreferredItemIndicatorRight">0dip</item>
+ <item name="expandableListPreferredChildIndicatorLeft">?attr/expandableListPreferredItemIndicatorLeft</item>
+ <item name="expandableListPreferredChildIndicatorRight">?attr/expandableListPreferredItemIndicatorRight</item>
+
+ <item name="listDividerAlertDialog">@drawable/list_divider_material</item>
+ <item name="findOnPageNextDrawable">@drawable/ic_find_next_material</item>
+ <item name="findOnPagePreviousDrawable">@drawable/ic_find_previous_material</item>
+
+ <!-- Gallery attributes -->
+ <item name="galleryItemBackground">@drawable/gallery_item_background</item>
+
+ <!-- Window attributes -->
+ <item name="windowBackground">@color/background_material_light</item>
+ <item name="windowFrame">@null</item>
+ <item name="windowNoTitle">false</item>
+ <item name="windowFullscreen">false</item>
+ <item name="windowOverscan">false</item>
+ <item name="windowIsFloating">false</item>
+ <item name="windowContentOverlay">@drawable/ab_solid_shadow_material</item>
+ <item name="windowShowWallpaper">false</item>
+ <item name="windowTitleStyle">@style/WindowTitle.Material</item>
+ <item name="windowTitleSize">@dimen/action_bar_default_height_material</item>
+ <item name="windowTitleBackgroundStyle">@style/WindowTitleBackground.Material</item>
+ <item name="windowAnimationStyle">@style/Animation.Material.Activity</item>
+ <item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+ <item name="windowActionBar">true</item>
+ <item name="windowActionModeOverlay">false</item>
+ <item name="windowDrawsSystemBarBackgrounds">true</item>
+ <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
+ <item name="statusBarColor">?attr/colorPrimaryDark</item>
+ <item name="navigationBarColor">@color/black</item>
+ <item name="windowEnterTransition">@transition/fade</item>
+ <item name="windowSharedElementEnterTransition">@transition/move</item>
+ <item name="windowSharedElementExitTransition">@transition/move</item>
+
+ <!-- Dialog attributes -->
+ <item name="dialogTheme">@style/Theme.Material.Light.Dialog</item>
+ <item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_material</item>
+ <item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_material</item>
+ <item name="dialogTitleDecorLayout">@layout/dialog_title_material</item>
+
+ <!-- AlertDialog attributes -->
+ <item name="alertDialogTheme">@style/Theme.Material.Light.Dialog.Alert</item>
+ <item name="alertDialogStyle">@style/AlertDialog.Material.Light</item>
+ <item name="alertDialogCenterButtons">false</item>
+ <item name="alertDialogIcon">@drawable/ic_dialog_alert_material</item>
+
+ <!-- Presentation attributes -->
+ <item name="presentationTheme">@style/Theme.Material.Light.Dialog.Presentation</item>
+
+ <!-- Toast attributes -->
+ <item name="toastFrameBackground">@drawable/toast_frame</item>
+
+ <!-- Panel attributes -->
+ <item name="panelBackground">?attr/colorBackground</item>
+ <item name="panelFullBackground">@drawable/menu_background_fill_parent_width</item>
+ <!-- These three attributes do not seems to be used by the framework. Declared public though -->
+ <item name="panelColorBackground">#000</item>
+ <item name="panelColorForeground">?attr/textColorPrimary</item>
+ <item name="panelTextAppearance">?attr/textAppearance</item>
+
+ <item name="panelMenuIsCompact">true</item>
+ <item name="panelMenuListWidth">250dip</item>
+ <item name="panelMenuListTheme">@style/Theme.Material.Light.CompactMenu</item>
+
+ <!-- Scrollbar attributes -->
+ <item name="scrollbarFadeDuration">250</item>
+ <item name="scrollbarDefaultDelayBeforeFade">300</item>
+ <item name="scrollbarSize">10dip</item>
+ <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
+ <item name="scrollbarThumbVertical">@drawable/scrollbar_handle_material</item>
+ <item name="scrollbarTrackHorizontal">@null</item>
+ <item name="scrollbarTrackVertical">@null</item>
+
+ <!-- Text selection handle attributes -->
+ <item name="textSelectHandleLeft">@drawable/text_select_handle_left_material</item>
+ <item name="textSelectHandleRight">@drawable/text_select_handle_right_material</item>
+ <item name="textSelectHandle">@drawable/text_select_handle_middle_material</item>
+ <item name="textSelectHandleWindowStyle">@style/Widget.Material.TextSelectHandle</item>
+ <item name="textSuggestionsWindowStyle">@style/Widget.Material.Light.TextSuggestionsPopupWindow</item>
+ <item name="textCursorDrawable">@drawable/text_cursor_material</item>
+
+ <!-- Widget styles -->
+ <item name="absListViewStyle">@style/Widget.Material.Light.AbsListView</item>
+ <item name="autoCompleteTextViewStyle">@style/Widget.Material.Light.AutoCompleteTextView</item>
+ <item name="checkboxStyle">@style/Widget.Material.Light.CompoundButton.CheckBox</item>
+ <item name="checkedTextViewStyle">@style/Widget.Material.Light.CheckedTextView</item>
+ <item name="dropDownListViewStyle">@style/Widget.Material.ListView.DropDown</item>
+ <item name="editTextStyle">@style/Widget.Material.Light.EditText</item>
+ <item name="expandableListViewStyle">@style/Widget.Material.Light.ExpandableListView</item>
+ <item name="expandableListViewWhiteStyle">@style/Widget.Material.Light.ExpandableListView.White</item>
+ <item name="fastScrollStyle">@style/Widget.Material.Light.FastScroll</item>
+ <item name="galleryStyle">@style/Widget.Material.Light.Gallery</item>
+ <item name="gestureOverlayViewStyle">@style/Widget.Material.Light.GestureOverlayView</item>
+ <item name="gridViewStyle">@style/Widget.Material.Light.GridView</item>
+ <item name="imageButtonStyle">@style/Widget.Material.Light.ImageButton</item>
+ <item name="imageWellStyle">@style/Widget.Material.Light.ImageWell</item>
+ <item name="listViewStyle">@style/Widget.Material.Light.ListView</item>
+ <item name="listViewWhiteStyle">@style/Widget.Material.Light.ListView.White</item>
+ <item name="popupWindowStyle">@style/Widget.Material.Light.PopupWindow</item>
+ <item name="progressBarStyle">@style/Widget.Material.Light.ProgressBar</item>
+ <item name="progressBarStyleHorizontal">@style/Widget.Material.Light.ProgressBar.Horizontal</item>
+ <item name="progressBarStyleSmall">@style/Widget.Material.Light.ProgressBar.Small</item>
+ <item name="progressBarStyleSmallTitle">@style/Widget.Material.Light.ProgressBar.Small.Title</item>
+ <item name="progressBarStyleLarge">@style/Widget.Material.Light.ProgressBar.Large</item>
+ <item name="progressBarStyleInverse">@style/Widget.Material.Light.ProgressBar.Inverse</item>
+ <item name="progressBarStyleSmallInverse">@style/Widget.Material.Light.ProgressBar.Small.Inverse</item>
+ <item name="progressBarStyleLargeInverse">@style/Widget.Material.Light.ProgressBar.Large.Inverse</item>
+ <item name="seekBarStyle">@style/Widget.Material.Light.SeekBar</item>
+ <item name="ratingBarStyle">@style/Widget.Material.Light.RatingBar</item>
+ <item name="ratingBarStyleIndicator">@style/Widget.Material.Light.RatingBar.Indicator</item>
+ <item name="ratingBarStyleSmall">@style/Widget.Material.Light.RatingBar.Small</item>
+ <item name="radioButtonStyle">@style/Widget.Material.Light.CompoundButton.RadioButton</item>
+ <item name="scrollViewStyle">@style/Widget.Material.Light.ScrollView</item>
+ <item name="horizontalScrollViewStyle">@style/Widget.Material.Light.HorizontalScrollView</item>
+ <item name="spinnerStyle">?attr/dropDownSpinnerStyle</item>
+ <item name="dropDownSpinnerStyle">@style/Widget.Material.Light.Spinner.DropDown</item>
+ <item name="starStyle">@style/Widget.Material.Light.CompoundButton.Star</item>
+ <item name="tabWidgetStyle">@style/Widget.Material.Light.TabWidget</item>
+ <item name="textViewStyle">@style/Widget.Material.Light.TextView</item>
+ <item name="errorMessageBackground">@drawable/popup_inline_error_holo_light</item>
+ <item name="errorMessageAboveBackground">@drawable/popup_inline_error_above_holo_light</item>
+ <item name="webTextViewStyle">@style/Widget.Material.Light.WebTextView</item>
+ <item name="webViewStyle">@style/Widget.Material.Light.WebView</item>
+ <item name="dropDownItemStyle">@style/Widget.Material.Light.DropDownItem</item>
+ <item name="spinnerDropDownItemStyle">@style/Widget.Material.Light.DropDownItem.Spinner</item>
+ <item name="spinnerItemStyle">@style/Widget.Material.TextView.SpinnerItem</item>
+ <item name="dropDownHintAppearance">@style/TextAppearance.Material.Widget.DropDownHint</item>
+ <item name="keyboardViewStyle">@style/Widget.Material.KeyboardView</item>
+ <item name="quickContactBadgeStyleWindowSmall">@style/Widget.Material.QuickContactBadge.WindowSmall</item>
+ <item name="quickContactBadgeStyleWindowMedium">@style/Widget.Material.QuickContactBadge.WindowMedium</item>
+ <item name="quickContactBadgeStyleWindowLarge">@style/Widget.Material.QuickContactBadge.WindowLarge</item>
+ <item name="quickContactBadgeStyleSmallWindowSmall">@style/Widget.Material.QuickContactBadgeSmall.WindowSmall</item>
+ <item name="quickContactBadgeStyleSmallWindowMedium">@style/Widget.Material.QuickContactBadgeSmall.WindowMedium</item>
+ <item name="quickContactBadgeStyleSmallWindowLarge">@style/Widget.Material.QuickContactBadgeSmall.WindowLarge</item>
+ <item name="listPopupWindowStyle">@style/Widget.Material.Light.ListPopupWindow</item>
+ <item name="popupMenuStyle">@style/Widget.Material.Light.PopupMenu</item>
+ <item name="stackViewStyle">@style/Widget.Material.Light.StackView</item>
+ <item name="activityChooserViewStyle">@style/Widget.Material.Light.ActivityChooserView</item>
+
+ <!-- Preference styles -->
+ <item name="preferenceScreenStyle">@style/Preference.Material.PreferenceScreen</item>
+ <item name="preferenceFragmentStyle">@style/PreferenceFragment.Material</item>
+ <item name="preferenceFragmentPaddingSide">0dip</item>
+ <item name="preferenceCategoryStyle">@style/Preference.Material.Category</item>
+ <item name="preferenceStyle">@style/Preference.Material</item>
+ <item name="preferenceInformationStyle">@style/Preference.Material.Information</item>
+ <item name="checkBoxPreferenceStyle">@style/Preference.Material.CheckBoxPreference</item>
+ <item name="switchPreferenceStyle">@style/Preference.Material.SwitchPreference</item>
+ <item name="yesNoPreferenceStyle">@style/Preference.Material.DialogPreference.YesNoPreference</item>
+ <item name="dialogPreferenceStyle">@style/Preference.Material.DialogPreference</item>
+ <item name="editTextPreferenceStyle">@style/Preference.Material.DialogPreference.EditTextPreference</item>
+ <item name="ringtonePreferenceStyle">@style/Preference.Material.RingtonePreference</item>
+ <item name="preferenceLayoutChild">@layout/preference_child_material</item>
+ <item name="detailsElementBackground">?attr/colorBackground</item>
+
+ <!-- PreferenceFrameLayout attributes -->
+ <item name="preferenceFrameLayoutStyle">@style/Widget.Material.PreferenceFrameLayout</item>
+
+ <!-- Search widget styles -->
+ <item name="searchWidgetCorpusItemBackground">@color/search_widget_corpus_item_background</item>
+
+ <!-- Action bar styles -->
+ <item name="actionDropDownStyle">@style/Widget.Material.Light.Spinner.DropDown.ActionBar</item>
+ <item name="actionButtonStyle">@style/Widget.Material.Light.ActionButton</item>
+ <item name="actionOverflowButtonStyle">@style/Widget.Material.Light.ActionButton.Overflow</item>
+ <item name="actionOverflowMenuStyle">@android:style/Widget.Material.Light.PopupMenu.Overflow</item>
+ <item name="actionModeBackground">@drawable/cab_background_top_holo_light</item>
+ <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
+ <item name="actionModeCloseDrawable">@drawable/ic_cab_done_material</item>
+ <item name="actionBarTabStyle">@style/Widget.Material.Light.ActionBar.TabView</item>
+ <item name="actionBarTabBarStyle">@style/Widget.Material.Light.ActionBar.TabBar</item>
+ <item name="actionBarTabTextStyle">@style/Widget.Material.Light.ActionBar.TabText</item>
+ <item name="actionModeStyle">@style/Widget.Material.Light.ActionMode</item>
+ <item name="actionModeCloseButtonStyle">@style/Widget.Material.Light.ActionButton.CloseMode</item>
+ <item name="actionBarStyle">@style/Widget.Material.Light.ActionBar.Solid</item>
+ <item name="actionBarSize">@dimen/action_bar_default_height_material</item>
+ <item name="actionModePopupWindowStyle">@style/Widget.Material.Light.PopupWindow.ActionMode</item>
+ <item name="actionBarWidgetTheme">@null</item>
+ <item name="actionBarTheme">@style/ThemeOverlay.Material.ActionBar</item>
+ <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
+
+ <item name="actionModeCutDrawable">@drawable/ic_menu_cut_material</item>
+ <item name="actionModeCopyDrawable">@drawable/ic_menu_copy_material</item>
+ <item name="actionModePasteDrawable">@drawable/ic_menu_paste_material</item>
+ <item name="actionModeSelectAllDrawable">@drawable/ic_menu_selectall_material</item>
+ <item name="actionModeShareDrawable">@drawable/ic_menu_share_material</item>
+ <item name="actionModeFindDrawable">@drawable/ic_menu_find_material</item>
+ <item name="actionModeWebSearchDrawable">@drawable/ic_menu_search_material</item>
+
+ <item name="toolbarStyle">@style/Widget.Material.Toolbar</item>
+
+ <item name="dividerVertical">?attr/listDivider</item>
+ <item name="dividerHorizontal">?attr/listDivider</item>
+ <item name="buttonBarStyle">@style/Widget.Material.Light.ButtonBar</item>
+ <item name="buttonBarButtonStyle">@style/Widget.Material.Light.Button.Borderless</item>
+ <item name="segmentedButtonStyle">@style/Widget.Material.Light.SegmentedButton</item>
+
+ <!-- SearchView attributes -->
+ <item name="searchDropdownBackground">?attr/colorBackground</item>
+ <item name="searchViewTextField">@drawable/textfield_search_material</item>
+ <item name="searchViewTextFieldRight">@drawable/textfield_search_material</item>
+ <item name="searchViewCloseIcon">@android:drawable/ic_clear_material</item>
+ <item name="searchViewSearchIcon">@android:drawable/ic_search_api_material</item>
+ <item name="searchViewGoIcon">@android:drawable/ic_go_search_api_material</item>
+ <item name="searchViewVoiceIcon">@android:drawable/ic_voice_search_api_material</item>
+ <item name="searchViewEditQuery">@android:drawable/ic_commit_search_api_material</item>
+
+ <item name="searchDialogTheme">@style/Theme.Material.Light.SearchBar</item>
+
+ <!-- NumberPicker style-->
+ <item name="numberPickerStyle">@style/Widget.Material.Light.NumberPicker</item>
+
+ <!-- CalendarView style-->
+ <item name="calendarViewStyle">@style/Widget.Material.Light.CalendarView</item>
+
+ <!-- TimePicker style -->
+ <item name="timePickerStyle">@style/Widget.Material.Light.TimePicker</item>
+
+ <!-- TimePicker Header background color -->
+ <item name="timePickerHeaderBackgroundColor">?attr/colorBackground</item>
+
+ <!-- TimePicker Header time label text appearance -->
+ <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
+
+ <!-- TimePicker Header am pm label text appearance -->
+ <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
+
+ <!-- TimePicker dialog theme -->
+ <item name="timePickerDialogTheme">@style/Theme.Material.Light.Dialog.TimePicker</item>
+
+ <!-- DatePicker style -->
+ <item name="datePickerStyle">@style/Widget.Material.Light.DatePicker</item>
+
+ <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_material</item>
+ <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
+ <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
+ <item name="fastScrollTrackDrawable">@drawable/fastscroll_track_material</item>
+ <item name="fastScrollOverlayPosition">atThumb</item>
+
+ <!-- Color palette -->
+ <item name="colorPrimaryDark">@color/material_blue_grey_600</item>
+ <item name="colorPrimary">@color/material_blue_grey_400</item>
+ <item name="colorAccent">@color/material_light_blue_A200</item>
+
+ <item name="colorControlNormal">?attr/textColorSecondary</item>
+ <item name="colorControlActivated">?attr/colorAccent</item>
+
+ <item name="colorControlHighlight">@color/ripple_material_light</item>
+ <item name="colorButtonNormal">@color/btn_default_material_light</item>
+ </style>
+
+ <!-- Variant of the material (light) theme that has a solid (opaque) action bar
+ with an inverse color profile. The dark action bar sharply stands out against
+ the light content. -->
+ <style name="Theme.Material.Light.DarkActionBar">
+ <item name="actionBarWidgetTheme">@null</item>
+ <item name="actionBarTheme">@style/ThemeOverlay.Material.Dark.ActionBar</item>
+
+ <item name="colorPrimaryDark">@color/material_blue_grey_900</item>
+ <item name="colorPrimary">@color/material_blue_grey_800</item>
+ </style>
+
+ <style name="ThemeOverlay" />
+ <style name="ThemeOverlay.Material" />
+
+ <!-- Theme overlay that replaces colors with their light versions but preserves
+ the value of colorAccent, colorPrimary and its variants. -->
+ <style name="ThemeOverlay.Material.Light">
+ <item name="colorForeground">@color/bright_foreground_material_light</item>
+ <item name="colorForegroundInverse">@color/bright_foreground_material_dark</item>
+ <item name="colorBackground">@color/background_material_light</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
+
+ <item name="textColorPrimary">@color/primary_text_material_light</item>
+ <item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
+ <item name="textColorSecondary">@color/secondary_text_material_light</item>
+ <item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item>
+ <item name="textColorTertiary">@color/tertiary_text_material_light</item>
+ <item name="textColorTertiaryInverse">@color/tertiary_text_material_dark</item>
+ <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_light</item>
+ <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_material_dark</item>
+ <item name="textColorHint">@color/hint_foreground_material_light</item>
+ <item name="textColorHintInverse">@color/hint_foreground_material_dark</item>
+ <item name="textColorHighlight">@color/highlighted_text_material_light</item>
+ <item name="textColorHighlightInverse">@color/highlighted_text_material_dark</item>
+ <item name="textColorLink">@color/material_teal_500</item>
+ <item name="textColorLinkInverse">@color/material_teal_500</item>
+ <item name="textColorSearchUrl">@color/search_url_text_material_light</item>
+ <item name="textColorAlertDialogListItem">@color/primary_text_material_light</item>
+
+ <item name="textCheckMark">@drawable/indicator_check_mark_light</item>
+ <item name="textCheckMarkInverse">@drawable/indicator_check_mark_dark</item>
+
+ <item name="windowBackground">@color/background_material_light</item>
+
+ <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
+ <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
+
+ <item name="colorControlHighlight">@color/ripple_material_light</item>
+ <item name="colorButtonNormal">@color/btn_default_material_light</item>
+ </style>
+
+ <!-- Theme overlay that replaces colors with their dark versions but preserves
+ the value of colorAccent, colorPrimary and its variants. -->
+ <style name="ThemeOverlay.Material.Dark">
+ <item name="colorForeground">@color/bright_foreground_material_dark</item>
+ <item name="colorForegroundInverse">@color/bright_foreground_material_light</item>
+ <item name="colorBackground">@color/background_material_dark</item>
+ <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
+
+ <item name="textColorPrimary">@color/primary_text_material_dark</item>
+ <item name="textColorPrimaryInverse">@color/primary_text_material_light</item>
+ <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
+ <item name="textColorSecondary">@color/secondary_text_material_dark</item>
+ <item name="textColorSecondaryInverse">@color/secondary_text_material_light</item>
+ <item name="textColorTertiary">@color/tertiary_text_material_dark</item>
+ <item name="textColorTertiaryInverse">@color/tertiary_text_material_light</item>
+ <item name="textColorHint">@color/hint_foreground_material_dark</item>
+ <item name="textColorHintInverse">@color/hint_foreground_material_light</item>
+ <item name="textColorHighlight">@color/highlighted_text_material_dark</item>
+ <item name="textColorHighlightInverse">@color/highlighted_text_material_light</item>
+ <item name="textColorLink">@color/material_teal_500</item>
+ <item name="textColorLinkInverse">@color/material_teal_500</item>
+ <item name="textColorSearchUrl">@color/search_url_text_material_dark</item>
+ <item name="textColorAlertDialogListItem">@color/primary_text_material_dark</item>
+
+ <item name="textCheckMark">@drawable/indicator_check_mark_dark</item>
+ <item name="textCheckMarkInverse">@drawable/indicator_check_mark_light</item>
+
+ <item name="windowBackground">@color/background_material_dark</item>
+
+ <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
+ <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
+
+ <item name="colorControlHighlight">@color/ripple_material_dark</item>
+ <item name="colorButtonNormal">@color/btn_default_material_dark</item>
+ </style>
+
+ <!-- Theme overlay that replaces the normal control color, which by default is the same as the
+ secondary text color, with the primary text color. -->
+ <style name="ThemeOverlay.Material.ActionBar">
+ <item name="colorControlNormal">?attr/textColorPrimary</item>
+ </style>
+
+ <!-- Theme overlay that replaces colors with their dark versions and replaces the normal
+ control color, which by default is the same as the secondary text color, with the primary
+ text color. -->
+ <style name="ThemeOverlay.Material.Dark.ActionBar">
+ <item name="colorControlNormal">?attr/textColorPrimary</item>
+ </style>
+
+ <!-- Variant of the material (dark) theme with no action bar. -->
+ <style name="Theme.Material.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <!-- Variant of the material (dark) theme that has no title bar and fills
+ the entire screen. This theme
+ sets {@link android.R.attr#windowFullscreen} to true. -->
+ <style name="Theme.Material.NoActionBar.Fullscreen">
+ <item name="windowFullscreen">true</item>
+ <item name="windowContentOverlay">@null</item>
+ </style>
+
+ <!-- Variant of the material (dark) theme that has no title bar and fills
+ the entire screen and extends into the display overscan region. This theme
+ sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
+ to true. -->
+ <style name="Theme.Material.NoActionBar.Overscan">
+ <item name="windowFullscreen">true</item>
+ <item name="windowOverscan">true</item>
+ <item name="windowContentOverlay">@null</item>
+ </style>
+
+ <!-- Variant of the material (dark) theme that has no title bar and translucent
+ system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
+ {@link android.R.attr#windowTranslucentNavigation} to true. -->
+ <style name="Theme.Material.NoActionBar.TranslucentDecor">
+ <item name="windowTranslucentStatus">true</item>
+ <item name="windowTranslucentNavigation">true</item>
+ <item name="windowContentOverlay">@null</item>
+ </style>
+
+ <!-- Variant of the material (light) theme with no action bar. -->
+ <style name="Theme.Material.Light.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <!-- Variant of the material (light) theme that has no title bar and fills
+ the entire screen. This theme
+ sets {@link android.R.attr#windowFullscreen} to true. -->
+ <style name="Theme.Material.Light.NoActionBar.Fullscreen">
+ <item name="windowFullscreen">true</item>
+ <item name="windowContentOverlay">@null</item>
+ </style>
+
+ <!-- Variant of the material (light) theme that has no title bar and fills
+ the entire screen and extends into the display overscan region. This theme
+ sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
+ to true. -->
+ <style name="Theme.Material.Light.NoActionBar.Overscan">
+ <item name="windowFullscreen">true</item>
+ <item name="windowOverscan">true</item>
+ <item name="windowContentOverlay">@null</item>
+ </style>
+
+ <!-- Variant of the material (light) theme that has no title bar and translucent
+ system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
+ {@link android.R.attr#windowTranslucentNavigation} to true. -->
+ <style name="Theme.Material.Light.NoActionBar.TranslucentDecor">
+ <item name="windowTranslucentStatus">true</item>
+ <item name="windowTranslucentNavigation">true</item>
+ <item name="windowContentOverlay">@null</item>
+ </style>
+
+ <!-- Default material dark theme for panel windows. This removes all extraneous
+ window decorations, so you basically have an empty rectangle in which
+ to place your content. It makes the window floating, with a transparent
+ background, and turns off dimming behind the window. -->
+ <style name="Theme.Material.Panel">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="windowFrame">@null</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="windowAnimationStyle">@null</item>
+ <item name="windowIsFloating">true</item>
+ <item name="backgroundDimEnabled">false</item>
+ <item name="windowIsTranslucent">true</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <!-- Default material light theme for panel windows. This removes all extraneous
+ window decorations, so you basically have an empty rectangle in which
+ to place your content. It makes the window floating, with a transparent
+ background, and turns off dimming behind the window. -->
+ <style name="Theme.Material.Light.Panel">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="windowFrame">@null</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="windowAnimationStyle">@null</item>
+ <item name="windowIsFloating">true</item>
+ <item name="backgroundDimEnabled">false</item>
+ <item name="windowIsTranslucent">true</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <!-- Material theme for an activity that is to be used for voice interaction.
+ This gives the activity a floating dialog style, to incorporate with the
+ system voice experience. -->
+ <style name="Theme.Material.Voice" parent="@style/Theme.Material.Dialog">
+ <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
+ <item name="backgroundDimEnabled">false</item>
+ </style>
+
+ <!-- Material light theme for an activity that is to be used for voice interaction.
+ This gives the activity a floating dialog style, to incorporate with the
+ system voice experience. -->
+ <style name="Theme.Material.Light.Voice" parent="@style/Theme.Material.Light.Dialog">
+ <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
+ <item name="backgroundDimEnabled">false</item>
+ </style>
+
+ <!-- Default theme for material style input methods, which is used by the
+ {@link android.inputmethodservice.InputMethodService} class.
+ this inherits from Theme.Panel, but sets up IME appropriate animations
+ and a few custom attributes. -->
+ <style name="Theme.Material.InputMethod" parent="Theme.Material.Light.Panel">
+ <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+ <item name="imeFullscreenBackground">@drawable/screen_background_selector_light</item>
+ <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+ <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
+ </style>
+
+ <!-- Default theme for material style voice interaction, which is used by the
+ {@link android.service.voice.VoiceInteractionSession} class.
+ this inherits from Theme.Panel, but sets up appropriate animations
+ and a few custom attributes. -->
+ <style name="Theme.Material.VoiceInteractionSession" parent="Theme.Material.Light.Panel">
+ <item name="windowAnimationStyle">@style/Animation.VoiceInteractionSession</item>
+ </style>
+
+ <!-- Theme for the search input bar. -->
+
+ <style name="Theme.Material.SearchBar" parent="Theme.Material.Panel">
+ <item name="actionModeBackground">@drawable/cab_background_top_holo_dark</item>
+ <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
+ </style>
+
+ <style name="Theme.Material.Light.SearchBar" parent="Theme.Material.Light.Panel">
+ <item name="actionModeBackground">@drawable/cab_background_top_holo_light</item>
+ <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
+ </style>
+
+ <!-- Menu Themes -->
+ <eat-comment />
+
+ <style name="Theme.Material.CompactMenu">
+ <!-- Menu/item attributes -->
+ <item name="itemTextAppearance">?attr/textAppearanceMedium</item>
+ <item name="listViewStyle">@style/Widget.Material.ListView</item>
+ <item name="windowAnimationStyle">@style/Animation.DropDownUp</item>
+ <item name="background">@null</item>
+ </style>
+
+ <style name="Theme.Material.Light.CompactMenu">
+ <!-- Menu/item attributes -->
+ <item name="itemTextAppearance">?attr/textAppearanceMedium</item>
+ <item name="listViewStyle">@style/Widget.Material.Light.ListView</item>
+ <item name="windowAnimationStyle">@style/Animation.DropDownUp</item>
+ <item name="background">@null</item>
+ </style>
+
+ <!-- Dialog themes for Material -->
+ <eat-comment />
+
+ <!-- Material theme for dialog windows and activities, which is used by the
+ {@link android.app.Dialog} class. This changes the window to be
+ floating (not fill the entire screen), and puts a frame around its
+ contents. You can set this theme on an activity if you would like to
+ make an activity that looks like a Dialog. -->
+ <style name="Theme.Material.Dialog">
+ <item name="windowFrame">@null</item>
+ <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
+ <item name="windowBackground">@drawable/dialog_background_shadow_material</item>
+ <item name="windowIsFloating">true</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
+ <item name="windowSoftInputMode">stateUnspecified|adjustPan</item>
+ <item name="windowActionBar">false</item>
+ <item name="windowActionModeOverlay">true</item>
+ <item name="windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
+
+ <item name="colorBackgroundCacheHint">@null</item>
+
+ <item name="buttonBarStyle">@style/Widget.Material.ButtonBar.AlertDialog</item>
+ <item name="borderlessButtonStyle">@style/Widget.Material.Button.Borderless</item>
+
+ <item name="textAppearance">@style/TextAppearance.Material</item>
+ <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
+
+ <item name="listPreferredItemPaddingLeft">16dip</item>
+ <item name="listPreferredItemPaddingRight">16dip</item>
+ <item name="listPreferredItemPaddingStart">16dip</item>
+ <item name="listPreferredItemPaddingEnd">16dip</item>
+
+ <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Dialog that has a nice minimum width for
+ a regular dialog. -->
+ <style name="Theme.Material.Dialog.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Dialog that does not include a title bar. -->
+ <style name="Theme.Material.Dialog.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Dialog.NoActionBar that has a nice minimum width for
+ a regular dialog. -->
+ <style name="Theme.Material.Dialog.NoActionBar.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Dialog that has a fixed size. -->
+ <style name="Theme.Material.Dialog.FixedSize">
+ <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
+ <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
+ <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
+ <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Dialog.NoActionBar that has a fixed size. -->
+ <style name="Theme.Material.Dialog.NoActionBar.FixedSize">
+ <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
+ <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
+ <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
+ <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Dialog that does not include a frame (or background).
+ The view hierarchy of the dialog is responsible for drawing all of
+ its pixels. -->
+ <style name="Theme.Material.Dialog.NoFrame">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="windowAnimationStyle">@null</item>
+ <item name="backgroundDimEnabled">false</item>
+ <item name="windowIsTranslucent">true</item>
+ <item name="windowNoTitle">true</item>
+ <item name="windowCloseOnTouchOutside">false</item>
+ </style>
+
+ <!-- Material theme for alert dialog windows, which is used by the
+ {@link android.app.AlertDialog} class. This is basically a dialog
+ but sets the background to empty so it can do two-tone backgrounds.
+ For applications targeting Honeycomb or newer, this is the default
+ AlertDialog theme. -->
+ <style name="Theme.Material.Dialog.Alert">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
+ </style>
+
+ <!-- Material theme for the TimePicker dialog windows, which is used by the
+ {@link android.app.TimePickerDialog} class. -->
+ <style name="Theme.Material.Dialog.TimePicker">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
+ <item name="windowContentOverlay">@null</item>
+ </style>
+
+ <!-- Theme for a window that will be displayed either full-screen on
+ smaller screens (small, normal) or as a dialog on larger screens
+ (large, xlarge). -->
+ <style name="Theme.Material.DialogWhenLarge" parent="@style/Theme.Material" />
+
+ <!-- Theme for a window without a title bar that will be displayed either
+ full-screen on smaller screens (small, normal) or as a dialog on larger screens
+ (large, xlarge). -->
+ <style name="Theme.Material.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.NoActionBar" />
+
+ <!-- Theme for a presentation window on a secondary display. -->
+ <style name="Theme.Material.Dialog.Presentation" parent="@style/Theme.Material.NoActionBar.Fullscreen" />
+
+ <!-- Light material dialog themes -->
+
+ <!-- Material light theme for dialog windows and activities, which is used by the
+ {@link android.app.Dialog} class. This changes the window to be
+ floating (not fill the entire screen), and puts a frame around its
+ contents. You can set this theme on an activity if you would like to
+ make an activity that looks like a Dialog. -->
+ <style name="Theme.Material.Light.Dialog">
+ <item name="windowFrame">@null</item>
+ <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
+ <item name="windowBackground">@drawable/dialog_background_shadow_material</item>
+ <item name="windowIsFloating">true</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
+ <item name="windowSoftInputMode">stateUnspecified|adjustPan</item>
+ <item name="windowActionBar">false</item>
+ <item name="windowActionModeOverlay">true</item>
+ <item name="windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
+
+ <item name="colorBackgroundCacheHint">@null</item>
+
+ <item name="buttonBarStyle">@style/Widget.Material.Light.ButtonBar.AlertDialog</item>
+ <item name="borderlessButtonStyle">@style/Widget.Material.Light.Button.Borderless</item>
+
+ <item name="textAppearance">@style/TextAppearance.Material</item>
+ <item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
+
+ <item name="listPreferredItemPaddingLeft">16dip</item>
+ <item name="listPreferredItemPaddingRight">16dip</item>
+ <item name="listPreferredItemPaddingStart">16dip</item>
+ <item name="listPreferredItemPaddingEnd">16dip</item>
+
+ <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Light.Dialog that has a nice minimum width for
+ a regular dialog. -->
+ <style name="Theme.Material.Light.Dialog.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Light.Dialog that does not include a title bar. -->
+ <style name="Theme.Material.Light.Dialog.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Light.Dialog.NoActionBar that has a nice minimum width for
+ a regular dialog. -->
+ <style name="Theme.Material.Light.Dialog.NoActionBar.MinWidth">
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Light.Dialog that has a fixed size. -->
+ <style name="Theme.Material.Light.Dialog.FixedSize">
+ <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
+ <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
+ <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
+ <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
+ </style>
+
+ <!-- Variant of Theme.Material.Light.Dialog.NoActionBar that has a fixed size. -->
+ <style name="Theme.Material.Light.Dialog.NoActionBar.FixedSize">
+ <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
+ <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
+ <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
+ <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
+ </style>
+
+ <!-- Theme for a window that will be displayed either full-screen on
+ smaller screens (small, normal) or as a dialog on larger screens
+ (large, xlarge). -->
+ <style name="Theme.Material.Light.DialogWhenLarge" parent="@style/Theme.Material.Light" />
+
+ <!-- Theme for a window without an action bar that will be displayed either full-screen
+ on smaller screens (small, normal) or as a dialog on larger screens
+ (large, xlarge). -->
+ <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Light.NoActionBar" />
+
+ <!-- Material light theme for alert dialog windows, which is used by the
+ {@link android.app.AlertDialog} class. This is basically a dialog
+ but sets the background to empty so it can do two-tone backgrounds.
+ For applications targeting Honeycomb or newer, this is the default
+ AlertDialog theme. -->
+ <style name="Theme.Material.Light.Dialog.Alert">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
+ <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+ <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
+ </style>
+
+ <!-- Material Light theme for the TimePicker dialog windows, which is used by the
+ {@link android.app.TimePickerDialog} class. -->
+ <style name="Theme.Material.Light.Dialog.TimePicker">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
+ </style>
+
+ <!-- Theme for a presentation window on a secondary display. -->
+ <style name="Theme.Material.Light.Dialog.Presentation" parent="@style/Theme.Material.Light.NoActionBar.Fullscreen" />
+
+ <!-- Default material (dark) for windows that want to have the user's selected
+ wallpaper appear behind them. -->
+ <style name="Theme.Material.Wallpaper">
+ <item name="windowBackground">@color/transparent</item>
+ <item name="colorBackgroundCacheHint">@null</item>
+ <item name="windowShowWallpaper">true</item>
+ </style>
+
+ <!--Default material (dark) for windows that want to have the user's selected
+ wallpaper appear behind them and without an action bar. -->
+ <style name="Theme.Material.Wallpaper.NoTitleBar">
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <!-- Default theme for Settings and activities launched from Settings. -->
+ <style name="Theme.Material.Settings" parent="@style/Theme.Material.Light.DarkActionBar">
+ <item name="colorPrimary">@color/material_blue_grey_900</item>
+ <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
+ <item name="colorAccent">@color/material_dark_teal_A400</item>
+ </style>
+
+</resources>
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
deleted file mode 100644
index cdbd771..0000000
--- a/core/res/res/values/themes_quantum.xml
+++ /dev/null
@@ -1,1225 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!--
-===============================================================
- PLEASE READ
-===============================================================
-
-The Quantum themes must not be modified in order to pass CTS.
-Many related themes and styles depend on other values defined in this file.
-If you would like to provide custom themes and styles for your device,
-please see themes_device_defaults.xml.
-
-===============================================================
- PLEASE READ
-===============================================================
- -->
-<resources>
-
- <!-- Quantum Paper theme (dark version).
- <p>If you want to ensure that your
- app consistently uses the Quantum theme at all times, you must explicitly declare it in your
- manifest. For example, {@code <application android:theme="@style/Theme.Quantum">}.
-
- <p>Styles used by the Quantum theme are named using the convention Type.Quantum.Etc
- (for example, {@code Widget.Quantum.Button} and {@code
- TextAppearance.Quantum.Widget.PopupMenu.Large}).
- Specific resources used by Quantum are named using the convention @type/foo_bar_baz_quantum
- with trailing _dark or _light specifiers if they are not shared between both light and
- dark versions of the theme. -->
- <style name="Theme.Quantum">
- <item name="colorForeground">@color/bright_foreground_quantum_dark</item>
- <item name="colorForegroundInverse">@color/bright_foreground_quantum_light</item>
- <item name="colorBackground">@color/background_quantum_dark</item>
- <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_quantum_dark</item>
- <item name="disabledAlpha">0.5</item>
- <item name="backgroundDimAmount">0.6</item>
-
- <!-- Text styles -->
- <item name="textAppearance">@style/TextAppearance.Quantum</item>
- <item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item>
-
- <item name="textColorPrimary">@color/primary_text_quantum_dark</item>
- <item name="textColorPrimaryInverse">@color/primary_text_quantum_light</item>
- <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_quantum_dark</item>
- <item name="textColorSecondary">@color/secondary_text_quantum_dark</item>
- <item name="textColorSecondaryInverse">@color/secondary_text_quantum_light</item>
- <item name="textColorTertiary">@color/tertiary_text_quantum_dark</item>
- <item name="textColorTertiaryInverse">@color/tertiary_text_quantum_light</item>
- <item name="textColorHint">@color/hint_foreground_quantum_dark</item>
- <item name="textColorHintInverse">@color/hint_foreground_quantum_light</item>
- <item name="textColorHighlight">@color/highlighted_text_quantum_dark</item>
- <item name="textColorHighlightInverse">@color/highlighted_text_quantum_light</item>
- <item name="textColorLink">@color/quantum_teal_500</item>
- <item name="textColorLinkInverse">@color/quantum_teal_500</item>
- <item name="textColorSearchUrl">@color/search_url_text_quantum_dark</item>
- <item name="textColorAlertDialogListItem">@color/primary_text_quantum_dark</item>
-
- <item name="textAppearanceLarge">@style/TextAppearance.Quantum.Large</item>
- <item name="textAppearanceLargeInverse">@style/TextAppearance.Quantum.Large.Inverse</item>
- <item name="textAppearanceMedium">@style/TextAppearance.Quantum.Medium</item>
- <item name="textAppearanceMediumInverse">@style/TextAppearance.Quantum.Medium.Inverse</item>
- <item name="textAppearanceSmall">@style/TextAppearance.Quantum.Small</item>
- <item name="textAppearanceSmallInverse">@style/TextAppearance.Quantum.Small.Inverse</item>
- <item name="textAppearanceSearchResultTitle">@style/TextAppearance.Quantum.SearchResult.Title</item>
- <item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.Quantum.SearchResult.Subtitle</item>
-
- <item name="textAppearanceButton">@style/TextAppearance.Quantum.Widget.Button</item>
-
- <item name="editTextColor">?attr/textColorPrimary</item>
- <item name="editTextBackground">@drawable/edit_text_quantum</item>
-
- <item name="candidatesTextStyleSpans">@string/candidates_style</item>
-
- <item name="textCheckMark">@drawable/indicator_check_mark_dark</item>
- <item name="textCheckMarkInverse">@drawable/indicator_check_mark_light</item>
-
- <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Quantum.Widget.PopupMenu.Large</item>
- <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Quantum.Widget.PopupMenu.Small</item>
-
- <!-- Button styles -->
- <item name="buttonStyle">@style/Widget.Quantum.Button</item>
- <item name="buttonStyleSmall">@style/Widget.Quantum.Button.Small</item>
- <item name="buttonStyleInset">@style/Widget.Quantum.Button.Inset</item>
- <item name="buttonStyleToggle">@style/Widget.Quantum.Button.Toggle</item>
-
- <item name="switchStyle">@style/Widget.Quantum.CompoundButton.Switch</item>
- <item name="mediaRouteButtonStyle">@style/Widget.Quantum.MediaRouteButton</item>
-
- <item name="selectableItemBackground">@drawable/item_background_quantum</item>
- <item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_quantum</item>
- <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless</item>
- <item name="homeAsUpIndicator">@drawable/ic_ab_back_quantum</item>
-
- <!-- List attributes -->
- <item name="listPreferredItemHeight">64dip</item>
- <item name="listPreferredItemHeightSmall">48dip</item>
- <item name="listPreferredItemHeightLarge">80dip</item>
- <item name="dropdownListPreferredItemHeight">?attr/listPreferredItemHeightSmall</item>
- <item name="textAppearanceListItem">@style/TextAppearance.Quantum.Subhead</item>
- <item name="textAppearanceListItemSmall">@style/TextAppearance.Quantum.Subhead</item>
- <item name="textAppearanceListItemSecondary">@style/TextAppearance.Quantum.Body1</item>
- <item name="listPreferredItemPaddingLeft">16dip</item>
- <item name="listPreferredItemPaddingRight">16dip</item>
- <item name="listPreferredItemPaddingStart">16dip</item>
- <item name="listPreferredItemPaddingEnd">16dip</item>
-
- <!-- @hide -->
- <item name="searchResultListItemHeight">58dip</item>
- <item name="listDivider">@drawable/list_divider_quantum</item>
- <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.TextView.ListSeparator</item>
-
- <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
- <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
-
- <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
- <item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
-
- <item name="listDividerAlertDialog">@drawable/list_divider_quantum</item>
-
- <item name="expandableListPreferredItemPaddingLeft">40dip</item>
- <item name="expandableListPreferredChildPaddingLeft">?attr/expandableListPreferredItemPaddingLeft</item>
-
- <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
- <item name="expandableListPreferredItemIndicatorRight">0dip</item>
- <item name="expandableListPreferredChildIndicatorLeft">?attr/expandableListPreferredItemIndicatorLeft</item>
- <item name="expandableListPreferredChildIndicatorRight">?attr/expandableListPreferredItemIndicatorRight</item>
- <item name="findOnPageNextDrawable">@drawable/ic_find_next_quantum</item>
- <item name="findOnPagePreviousDrawable">@drawable/ic_find_previous_quantum</item>
-
- <!-- Gallery attributes -->
- <item name="galleryItemBackground">@drawable/gallery_item_background</item>
-
- <!-- Window attributes -->
- <item name="windowBackground">@color/background_quantum_dark</item>
- <item name="windowFrame">@null</item>
- <item name="windowNoTitle">false</item>
- <item name="windowFullscreen">false</item>
- <item name="windowOverscan">false</item>
- <item name="windowIsFloating">false</item>
- <item name="windowContentOverlay">@null</item>
- <item name="windowShowWallpaper">false</item>
- <item name="windowTitleStyle">@style/WindowTitle.Quantum</item>
- <item name="windowTitleSize">25dip</item>
- <item name="windowTitleBackgroundStyle">@style/WindowTitleBackground.Quantum</item>
- <item name="windowContentTransitions">false</item>
- <item name="windowAnimationStyle">@style/Animation.Quantum.Activity</item>
- <item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
- <item name="windowActionBar">true</item>
- <item name="windowActionModeOverlay">false</item>
- <item name="windowDrawsSystemBarBackgrounds">true</item>
- <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
- <item name="statusBarColor">?attr/colorPrimaryDark</item>
- <item name="navigationBarColor">?attr/colorPrimaryDark</item>
-
- <!-- Dialog attributes -->
- <item name="dialogTheme">@style/Theme.Quantum.Dialog</item>
- <item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_quantum</item>
- <item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_quantum</item>
- <item name="dialogTitleDecorLayout">@layout/dialog_title_quantum</item>
-
- <!-- AlertDialog attributes -->
- <item name="alertDialogTheme">@style/Theme.Quantum.Dialog.Alert</item>
- <item name="alertDialogStyle">@style/AlertDialog.Quantum</item>
- <item name="alertDialogCenterButtons">false</item>
- <item name="alertDialogIcon">@drawable/ic_dialog_alert_quantum</item>
-
- <!-- Presentation attributes -->
- <item name="presentationTheme">@style/Theme.Quantum.Dialog.Presentation</item>
-
- <!-- Toast attributes -->
- <item name="toastFrameBackground">@drawable/toast_frame</item>
-
- <!-- Panel attributes -->
- <item name="panelBackground">?attr/colorBackground</item>
- <item name="panelFullBackground">@drawable/menu_background_fill_parent_width</item>
- <!-- These three attributes do not seems to be used by the framework. Declared public though -->
- <item name="panelColorBackground">#000</item>
- <item name="panelColorForeground">?attr/textColorPrimary</item>
- <item name="panelTextAppearance">?attr/textAppearance</item>
-
- <item name="panelMenuIsCompact">true</item>
- <item name="panelMenuListWidth">250dip</item>
- <item name="panelMenuListTheme">@style/Theme.Quantum.CompactMenu</item>
-
- <!-- Scrollbar attributes -->
- <item name="scrollbarFadeDuration">250</item>
- <item name="scrollbarDefaultDelayBeforeFade">300</item>
- <item name="scrollbarSize">10dip</item>
- <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_quantum</item>
- <item name="scrollbarThumbVertical">@drawable/scrollbar_handle_quantum</item>
- <item name="scrollbarTrackHorizontal">@null</item>
- <item name="scrollbarTrackVertical">@null</item>
-
- <!-- Text selection handle attributes -->
- <item name="textSelectHandleLeft">@drawable/text_select_handle_left_quantum</item>
- <item name="textSelectHandleRight">@drawable/text_select_handle_right_quantum</item>
- <item name="textSelectHandle">@drawable/text_select_handle_middle_quantum</item>
- <item name="textSelectHandleWindowStyle">@style/Widget.Quantum.TextSelectHandle</item>
- <item name="textSuggestionsWindowStyle">@style/Widget.Quantum.TextSuggestionsPopupWindow</item>
- <item name="textCursorDrawable">@drawable/text_cursor_quantum</item>
-
- <!-- Widget styles -->
- <item name="absListViewStyle">@style/Widget.Quantum.AbsListView</item>
- <item name="autoCompleteTextViewStyle">@style/Widget.Quantum.AutoCompleteTextView</item>
- <item name="checkboxStyle">@style/Widget.Quantum.CompoundButton.CheckBox</item>
- <item name="checkedTextViewStyle">@style/Widget.Quantum.CheckedTextView</item>
- <item name="dropDownListViewStyle">@style/Widget.Quantum.ListView.DropDown</item>
- <item name="editTextStyle">@style/Widget.Quantum.EditText</item>
- <item name="expandableListViewStyle">@style/Widget.Quantum.ExpandableListView</item>
- <item name="expandableListViewWhiteStyle">@style/Widget.Quantum.ExpandableListView.White</item>
- <item name="fastScrollStyle">@style/Widget.Quantum.FastScroll</item>
- <item name="galleryStyle">@style/Widget.Quantum.Gallery</item>
- <item name="gestureOverlayViewStyle">@style/Widget.Quantum.GestureOverlayView</item>
- <item name="gridViewStyle">@style/Widget.Quantum.GridView</item>
- <item name="imageButtonStyle">@style/Widget.Quantum.ImageButton</item>
- <item name="imageWellStyle">@style/Widget.Quantum.ImageWell</item>
- <item name="listViewStyle">@style/Widget.Quantum.ListView</item>
- <item name="listViewWhiteStyle">@style/Widget.Quantum.ListView.White</item>
- <item name="popupWindowStyle">@style/Widget.Quantum.PopupWindow</item>
- <item name="progressBarStyle">@style/Widget.Quantum.ProgressBar</item>
- <item name="progressBarStyleHorizontal">@style/Widget.Quantum.ProgressBar.Horizontal</item>
- <item name="progressBarStyleSmall">@style/Widget.Quantum.ProgressBar.Small</item>
- <item name="progressBarStyleSmallTitle">@style/Widget.Quantum.ProgressBar.Small.Title</item>
- <item name="progressBarStyleLarge">@style/Widget.Quantum.ProgressBar.Large</item>
- <item name="progressBarStyleInverse">@style/Widget.Quantum.ProgressBar.Inverse</item>
- <item name="progressBarStyleSmallInverse">@style/Widget.Quantum.ProgressBar.Small.Inverse</item>
- <item name="progressBarStyleLargeInverse">@style/Widget.Quantum.ProgressBar.Large.Inverse</item>
- <item name="seekBarStyle">@style/Widget.Quantum.SeekBar</item>
- <item name="ratingBarStyle">@style/Widget.Quantum.RatingBar</item>
- <item name="ratingBarStyleIndicator">@style/Widget.Quantum.RatingBar.Indicator</item>
- <item name="ratingBarStyleSmall">@style/Widget.Quantum.RatingBar.Small</item>
- <item name="radioButtonStyle">@style/Widget.Quantum.CompoundButton.RadioButton</item>
- <item name="scrollViewStyle">@style/Widget.Quantum.ScrollView</item>
- <item name="horizontalScrollViewStyle">@style/Widget.Quantum.HorizontalScrollView</item>
- <item name="spinnerStyle">?attr/dropDownSpinnerStyle</item>
- <item name="dropDownSpinnerStyle">@style/Widget.Quantum.Spinner.DropDown</item>
- <item name="starStyle">@style/Widget.Quantum.CompoundButton.Star</item>
- <item name="tabWidgetStyle">@style/Widget.Quantum.TabWidget</item>
- <item name="textViewStyle">@style/Widget.Quantum.TextView</item>
- <item name="errorMessageBackground">@drawable/popup_inline_error_holo_dark</item>
- <item name="errorMessageAboveBackground">@drawable/popup_inline_error_above_holo_dark</item>
- <item name="webTextViewStyle">@style/Widget.Quantum.WebTextView</item>
- <item name="webViewStyle">@style/Widget.Quantum.WebView</item>
- <item name="dropDownItemStyle">@style/Widget.Quantum.DropDownItem</item>
- <item name="spinnerDropDownItemStyle">@style/Widget.Quantum.DropDownItem.Spinner</item>
- <item name="spinnerItemStyle">@style/Widget.Quantum.TextView.SpinnerItem</item>
- <item name="dropDownHintAppearance">@style/TextAppearance.Quantum.Widget.DropDownHint</item>
- <item name="keyboardViewStyle">@style/Widget.Quantum.KeyboardView</item>
- <item name="quickContactBadgeStyleWindowSmall">@style/Widget.Quantum.QuickContactBadge.WindowSmall</item>
- <item name="quickContactBadgeStyleWindowMedium">@style/Widget.Quantum.QuickContactBadge.WindowMedium</item>
- <item name="quickContactBadgeStyleWindowLarge">@style/Widget.Quantum.QuickContactBadge.WindowLarge</item>
- <item name="quickContactBadgeStyleSmallWindowSmall">@style/Widget.Quantum.QuickContactBadgeSmall.WindowSmall</item>
- <item name="quickContactBadgeStyleSmallWindowMedium">@style/Widget.Quantum.QuickContactBadgeSmall.WindowMedium</item>
- <item name="quickContactBadgeStyleSmallWindowLarge">@style/Widget.Quantum.QuickContactBadgeSmall.WindowLarge</item>
- <item name="listPopupWindowStyle">@style/Widget.Quantum.ListPopupWindow</item>
- <item name="popupMenuStyle">@style/Widget.Quantum.PopupMenu</item>
- <item name="stackViewStyle">@style/Widget.Quantum.StackView</item>
- <item name="activityChooserViewStyle">@style/Widget.Quantum.ActivityChooserView</item>
-
- <!-- Preference styles -->
- <item name="preferenceScreenStyle">@style/Preference.Quantum.PreferenceScreen</item>
- <item name="preferenceFragmentStyle">@style/PreferenceFragment.Quantum</item>
- <item name="preferenceFragmentPaddingSide">0dip</item>
- <item name="preferenceCategoryStyle">@style/Preference.Quantum.Category</item>
- <item name="preferenceStyle">@style/Preference.Quantum</item>
- <item name="preferenceInformationStyle">@style/Preference.Quantum.Information</item>
- <item name="checkBoxPreferenceStyle">@style/Preference.Quantum.CheckBoxPreference</item>
- <item name="switchPreferenceStyle">@style/Preference.Quantum.SwitchPreference</item>
- <item name="yesNoPreferenceStyle">@style/Preference.Quantum.DialogPreference.YesNoPreference</item>
- <item name="dialogPreferenceStyle">@style/Preference.Quantum.DialogPreference</item>
- <item name="editTextPreferenceStyle">@style/Preference.Quantum.DialogPreference.EditTextPreference</item>
- <item name="ringtonePreferenceStyle">@style/Preference.Quantum.RingtonePreference</item>
- <item name="preferenceLayoutChild">@layout/preference_child_quantum</item>
- <item name="detailsElementBackground">?attr/colorBackground</item>
-
- <!-- Search widget styles -->
- <item name="searchWidgetCorpusItemBackground">@color/search_widget_corpus_item_background</item>
-
- <!-- Action bar styles -->
- <item name="actionDropDownStyle">@style/Widget.Quantum.Spinner.DropDown.ActionBar</item>
- <item name="actionButtonStyle">@style/Widget.Quantum.ActionButton</item>
- <item name="actionOverflowButtonStyle">@style/Widget.Quantum.ActionButton.Overflow</item>
- <item name="actionOverflowMenuStyle">@android:style/Widget.Quantum.PopupMenu.Overflow</item>
- <item name="actionModeBackground">?attr/colorPrimaryDark</item>
- <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
- <item name="actionModeCloseDrawable">@drawable/ic_cab_done_quantum</item>
- <item name="actionBarTabStyle">@style/Widget.Quantum.ActionBar.TabView</item>
- <item name="actionBarTabBarStyle">@style/Widget.Quantum.ActionBar.TabBar</item>
- <item name="actionBarTabTextStyle">@style/Widget.Quantum.ActionBar.TabText</item>
- <item name="actionModeStyle">@style/Widget.Quantum.ActionMode</item>
- <item name="actionModeCloseButtonStyle">@style/Widget.Quantum.ActionButton.CloseMode</item>
- <item name="actionBarStyle">@style/Widget.Quantum.ActionBar.Solid</item>
- <item name="actionBarSize">@dimen/action_bar_default_height_quantum</item>
- <item name="actionModePopupWindowStyle">@style/Widget.Quantum.PopupWindow.ActionMode</item>
- <item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item>
- <item name="actionBarTheme">@null</item>
- <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
-
- <item name="actionModeCutDrawable">@drawable/ic_menu_cut_quantum</item>
- <item name="actionModeCopyDrawable">@drawable/ic_menu_copy_quantum</item>
- <item name="actionModePasteDrawable">@drawable/ic_menu_paste_quantum</item>
- <item name="actionModeSelectAllDrawable">@drawable/ic_menu_selectall_quantum</item>
- <item name="actionModeShareDrawable">@drawable/ic_menu_share_quantum</item>
- <item name="actionModeFindDrawable">@drawable/ic_menu_find_quantum</item>
- <item name="actionModeWebSearchDrawable">@drawable/ic_menu_search_quantum</item>
-
- <item name="dividerVertical">?attr/listDivider</item>
- <item name="dividerHorizontal">?attr/listDivider</item>
- <item name="buttonBarStyle">@style/Widget.Quantum.ButtonBar</item>
- <item name="buttonBarButtonStyle">@style/Widget.Quantum.Button.Borderless</item>
- <item name="segmentedButtonStyle">@style/Widget.Quantum.SegmentedButton</item>
-
- <!-- SearchView attributes -->
- <item name="searchDropdownBackground">?attr/colorBackground</item>
- <item name="searchViewTextField">@drawable/textfield_search_quantum</item>
- <item name="searchViewTextFieldRight">@drawable/textfield_search_quantum</item>
- <item name="searchViewCloseIcon">@android:drawable/ic_clear_quantum</item>
- <item name="searchViewSearchIcon">@android:drawable/ic_search_api_quantum</item>
- <item name="searchViewGoIcon">@android:drawable/ic_go_search_api_quantum</item>
- <item name="searchViewVoiceIcon">@android:drawable/ic_voice_search_api_quantum</item>
- <item name="searchViewEditQuery">@android:drawable/ic_commit_search_api_quantum</item>
-
- <item name="searchDialogTheme">@style/Theme.Quantum.SearchBar</item>
-
- <!-- PreferenceFrameLayout attributes -->
- <item name="preferenceFrameLayoutStyle">@style/Widget.Quantum.PreferenceFrameLayout</item>
-
- <!-- NumberPicker style-->
- <item name="numberPickerStyle">@style/Widget.Quantum.NumberPicker</item>
-
- <!-- CalendarView style-->
- <item name="calendarViewStyle">@style/Widget.Quantum.CalendarView</item>
-
- <!-- TimePicker style -->
- <item name="timePickerStyle">@style/Widget.Quantum.TimePicker</item>
-
- <!-- TimePicker background color -->
- <item name="timePickerHeaderBackgroundColor">?colorBackground</item>
-
- <!-- TimePicker Header time label text appearance -->
- <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Quantum.TimePicker.TimeLabel</item>
-
- <!-- TimePicker Header am pm label text appearance -->
- <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.Quantum.TimePicker.AmPmLabel</item>
-
- <!-- TimePicker dialog theme -->
- <item name="timePickerDialogTheme">@style/Theme.Quantum.Dialog.TimePicker</item>
-
- <!-- DatePicker style -->
- <item name="datePickerStyle">@style/Widget.Quantum.DatePicker</item>
-
- <!-- TODO: This belongs in a FastScroll style -->
- <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_quantum</item>
- <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
- <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
- <item name="fastScrollTrackDrawable">@drawable/fastscroll_track_quantum</item>
- <item name="fastScrollOverlayPosition">atThumb</item>
-
- <!-- Color palette -->
- <item name="colorPrimaryDark">@color/quantum_blue_700</item>
- <item name="colorPrimary">@color/quantum_blue_500</item>
- <item name="colorPrimaryLight">@color/quantum_blue_100</item>
- <item name="colorAccent">@color/quantum_teal_A200</item>
-
- <item name="colorControlNormal">?attr/textColorSecondary</item>
- <item name="colorControlActivated">?attr/colorPrimary</item>
-
- <item name="colorControlHighlight">@color/ripple_quantum_dark</item>
- <item name="colorButtonNormal">@color/btn_default_quantum_dark</item>
- </style>
-
- <!-- Quantum Paper theme (light version). -->
- <style name="Theme.Quantum.Light" parent="Theme.Light">
- <item name="colorForeground">@color/bright_foreground_quantum_light</item>
- <item name="colorForegroundInverse">@color/bright_foreground_quantum_dark</item>
- <item name="colorBackground">@color/background_quantum_light</item>
- <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_quantum_light</item>
- <item name="disabledAlpha">0.5</item>
- <item name="backgroundDimAmount">0.6</item>
-
- <!-- Text styles -->
- <item name="textAppearance">@style/TextAppearance.Quantum</item>
- <item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item>
-
- <item name="textColorPrimary">@color/primary_text_quantum_light</item>
- <item name="textColorPrimaryInverse">@color/primary_text_quantum_dark</item>
- <item name="textColorSecondary">@color/secondary_text_quantum_light</item>
- <item name="textColorSecondaryInverse">@color/secondary_text_quantum_dark</item>
- <item name="textColorTertiary">@color/tertiary_text_quantum_light</item>
- <item name="textColorTertiaryInverse">@color/tertiary_text_quantum_dark</item>
- <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_quantum_light</item>
- <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_quantum_dark</item>
- <item name="textColorHint">@color/hint_foreground_quantum_light</item>
- <item name="textColorHintInverse">@color/hint_foreground_quantum_dark</item>
- <item name="textColorHighlight">@color/highlighted_text_quantum_light</item>
- <item name="textColorHighlightInverse">@color/highlighted_text_quantum_dark</item>
- <item name="textColorLink">@color/quantum_teal_500</item>
- <item name="textColorLinkInverse">@color/quantum_teal_500</item>
- <item name="textColorSearchUrl">@color/search_url_text_quantum_light</item>
- <item name="textColorAlertDialogListItem">@color/primary_text_quantum_light</item>
-
- <item name="textAppearanceLarge">@style/TextAppearance.Quantum.Large</item>
- <item name="textAppearanceLargeInverse">@style/TextAppearance.Quantum.Large.Inverse</item>
- <item name="textAppearanceMedium">@style/TextAppearance.Quantum.Medium</item>
- <item name="textAppearanceMediumInverse">@style/TextAppearance.Quantum.Medium.Inverse</item>
- <item name="textAppearanceSmall">@style/TextAppearance.Quantum.Small</item>
- <item name="textAppearanceSmallInverse">@style/TextAppearance.Quantum.Small.Inverse</item>
- <item name="textAppearanceSearchResultTitle">@style/TextAppearance.Quantum.SearchResult.Title</item>
- <item name="textAppearanceSearchResultSubtitle">@style/TextAppearance.Quantum.SearchResult.Subtitle</item>
-
- <item name="textAppearanceButton">@style/TextAppearance.Quantum.Widget.Button</item>
-
- <item name="editTextColor">?attr/textColorPrimary</item>
- <item name="editTextBackground">@drawable/edit_text_quantum</item>
-
- <item name="candidatesTextStyleSpans">@string/candidates_style</item>
-
- <item name="textCheckMark">@drawable/indicator_check_mark_light</item>
- <item name="textCheckMarkInverse">@drawable/indicator_check_mark_dark</item>
-
- <item name="textAppearanceLargePopupMenu">@style/TextAppearance.Quantum.Widget.PopupMenu.Large</item>
- <item name="textAppearanceSmallPopupMenu">@style/TextAppearance.Quantum.Widget.PopupMenu.Small</item>
-
- <!-- Button styles -->
- <item name="buttonStyle">@style/Widget.Quantum.Light.Button</item>
-
- <item name="buttonStyleSmall">@style/Widget.Quantum.Light.Button.Small</item>
- <item name="buttonStyleInset">@style/Widget.Quantum.Light.Button.Inset</item>
-
- <item name="buttonStyleToggle">@style/Widget.Quantum.Light.Button.Toggle</item>
- <item name="switchStyle">@style/Widget.Quantum.Light.CompoundButton.Switch</item>
- <item name="mediaRouteButtonStyle">@style/Widget.Quantum.Light.MediaRouteButton</item>
-
- <item name="selectableItemBackground">@drawable/item_background_quantum</item>
- <item name="selectableItemBackgroundBorderless">@drawable/item_background_borderless_quantum</item>
- <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
- <item name="homeAsUpIndicator">@drawable/ic_ab_back_quantum</item>
-
- <!-- List attributes -->
- <item name="listPreferredItemHeight">64dip</item>
- <item name="listPreferredItemHeightSmall">48dip</item>
- <item name="listPreferredItemHeightLarge">80dip</item>
- <item name="dropdownListPreferredItemHeight">?attr/listPreferredItemHeightSmall</item>
- <item name="textAppearanceListItem">@style/TextAppearance.Quantum.Subhead</item>
- <item name="textAppearanceListItemSmall">@style/TextAppearance.Quantum.Subhead</item>
- <item name="textAppearanceListItemSecondary">@style/TextAppearance.Quantum.Body1</item>
- <item name="listPreferredItemPaddingLeft">16dip</item>
- <item name="listPreferredItemPaddingRight">16dip</item>
- <item name="listPreferredItemPaddingStart">16dip</item>
- <item name="listPreferredItemPaddingEnd">16dip</item>
-
- <!-- @hide -->
- <item name="searchResultListItemHeight">58dip</item>
- <item name="listDivider">@drawable/list_divider_quantum</item>
- <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.Light.TextView.ListSeparator</item>
-
- <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
- <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
-
- <item name="listChoiceBackgroundIndicator">?attr/selectableItemBackground</item>
- <item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
-
- <item name="expandableListPreferredItemPaddingLeft">40dip</item>
- <item name="expandableListPreferredChildPaddingLeft">?attr/expandableListPreferredItemPaddingLeft</item>
-
- <item name="expandableListPreferredItemIndicatorLeft">3dip</item>
- <item name="expandableListPreferredItemIndicatorRight">0dip</item>
- <item name="expandableListPreferredChildIndicatorLeft">?attr/expandableListPreferredItemIndicatorLeft</item>
- <item name="expandableListPreferredChildIndicatorRight">?attr/expandableListPreferredItemIndicatorRight</item>
-
- <item name="listDividerAlertDialog">@drawable/list_divider_quantum</item>
- <item name="findOnPageNextDrawable">@drawable/ic_find_next_quantum</item>
- <item name="findOnPagePreviousDrawable">@drawable/ic_find_previous_quantum</item>
-
- <!-- Gallery attributes -->
- <item name="galleryItemBackground">@drawable/gallery_item_background</item>
-
- <!-- Window attributes -->
- <item name="windowBackground">@color/background_quantum_light</item>
- <item name="windowFrame">@null</item>
- <item name="windowNoTitle">false</item>
- <item name="windowFullscreen">false</item>
- <item name="windowOverscan">false</item>
- <item name="windowIsFloating">false</item>
- <item name="windowContentOverlay">@drawable/ab_solid_shadow_quantum</item>
- <item name="windowShowWallpaper">false</item>
- <item name="windowTitleStyle">@style/WindowTitle.Quantum</item>
- <item name="windowTitleSize">25dip</item>
- <item name="windowTitleBackgroundStyle">@style/WindowTitleBackground.Quantum</item>
- <item name="windowAnimationStyle">@style/Animation.Quantum.Activity</item>
- <item name="windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
- <item name="windowActionBar">true</item>
- <item name="windowActionModeOverlay">false</item>
- <item name="windowDrawsSystemBarBackgrounds">true</item>
- <item name="windowActionBarFullscreenDecorLayout">@layout/screen_toolbar</item>
- <item name="statusBarColor">?attr/colorPrimaryDark</item>
- <item name="navigationBarColor">?attr/colorPrimaryDark</item>
-
- <!-- Dialog attributes -->
- <item name="dialogTheme">@style/Theme.Quantum.Light.Dialog</item>
- <item name="dialogTitleIconsDecorLayout">@layout/dialog_title_icons_quantum</item>
- <item name="dialogCustomTitleDecorLayout">@layout/dialog_custom_title_quantum</item>
- <item name="dialogTitleDecorLayout">@layout/dialog_title_quantum</item>
-
- <!-- AlertDialog attributes -->
- <item name="alertDialogTheme">@style/Theme.Quantum.Light.Dialog.Alert</item>
- <item name="alertDialogStyle">@style/AlertDialog.Quantum.Light</item>
- <item name="alertDialogCenterButtons">false</item>
- <item name="alertDialogIcon">@drawable/ic_dialog_alert_quantum</item>
-
- <!-- Presentation attributes -->
- <item name="presentationTheme">@style/Theme.Quantum.Light.Dialog.Presentation</item>
-
- <!-- Toast attributes -->
- <item name="toastFrameBackground">@drawable/toast_frame</item>
-
- <!-- Panel attributes -->
- <item name="panelBackground">?attr/colorBackground</item>
- <item name="panelFullBackground">@drawable/menu_background_fill_parent_width</item>
- <!-- These three attributes do not seems to be used by the framework. Declared public though -->
- <item name="panelColorBackground">#000</item>
- <item name="panelColorForeground">?attr/textColorPrimary</item>
- <item name="panelTextAppearance">?attr/textAppearance</item>
-
- <item name="panelMenuIsCompact">true</item>
- <item name="panelMenuListWidth">250dip</item>
- <item name="panelMenuListTheme">@style/Theme.Quantum.Light.CompactMenu</item>
-
- <!-- Scrollbar attributes -->
- <item name="scrollbarFadeDuration">250</item>
- <item name="scrollbarDefaultDelayBeforeFade">300</item>
- <item name="scrollbarSize">10dip</item>
- <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_quantum</item>
- <item name="scrollbarThumbVertical">@drawable/scrollbar_handle_quantum</item>
- <item name="scrollbarTrackHorizontal">@null</item>
- <item name="scrollbarTrackVertical">@null</item>
-
- <!-- Text selection handle attributes -->
- <item name="textSelectHandleLeft">@drawable/text_select_handle_left_quantum</item>
- <item name="textSelectHandleRight">@drawable/text_select_handle_right_quantum</item>
- <item name="textSelectHandle">@drawable/text_select_handle_middle_quantum</item>
- <item name="textSelectHandleWindowStyle">@style/Widget.Quantum.TextSelectHandle</item>
- <item name="textSuggestionsWindowStyle">@style/Widget.Quantum.Light.TextSuggestionsPopupWindow</item>
- <item name="textCursorDrawable">@drawable/text_cursor_quantum</item>
-
- <!-- Widget styles -->
- <item name="absListViewStyle">@style/Widget.Quantum.Light.AbsListView</item>
- <item name="autoCompleteTextViewStyle">@style/Widget.Quantum.Light.AutoCompleteTextView</item>
- <item name="checkboxStyle">@style/Widget.Quantum.Light.CompoundButton.CheckBox</item>
- <item name="checkedTextViewStyle">@style/Widget.Quantum.Light.CheckedTextView</item>
- <item name="dropDownListViewStyle">@style/Widget.Quantum.ListView.DropDown</item>
- <item name="editTextStyle">@style/Widget.Quantum.Light.EditText</item>
- <item name="expandableListViewStyle">@style/Widget.Quantum.Light.ExpandableListView</item>
- <item name="expandableListViewWhiteStyle">@style/Widget.Quantum.Light.ExpandableListView.White</item>
- <item name="fastScrollStyle">@style/Widget.Quantum.Light.FastScroll</item>
- <item name="galleryStyle">@style/Widget.Quantum.Light.Gallery</item>
- <item name="gestureOverlayViewStyle">@style/Widget.Quantum.Light.GestureOverlayView</item>
- <item name="gridViewStyle">@style/Widget.Quantum.Light.GridView</item>
- <item name="imageButtonStyle">@style/Widget.Quantum.Light.ImageButton</item>
- <item name="imageWellStyle">@style/Widget.Quantum.Light.ImageWell</item>
- <item name="listViewStyle">@style/Widget.Quantum.Light.ListView</item>
- <item name="listViewWhiteStyle">@style/Widget.Quantum.Light.ListView.White</item>
- <item name="popupWindowStyle">@style/Widget.Quantum.Light.PopupWindow</item>
- <item name="progressBarStyle">@style/Widget.Quantum.Light.ProgressBar</item>
- <item name="progressBarStyleHorizontal">@style/Widget.Quantum.Light.ProgressBar.Horizontal</item>
- <item name="progressBarStyleSmall">@style/Widget.Quantum.Light.ProgressBar.Small</item>
- <item name="progressBarStyleSmallTitle">@style/Widget.Quantum.Light.ProgressBar.Small.Title</item>
- <item name="progressBarStyleLarge">@style/Widget.Quantum.Light.ProgressBar.Large</item>
- <item name="progressBarStyleInverse">@style/Widget.Quantum.Light.ProgressBar.Inverse</item>
- <item name="progressBarStyleSmallInverse">@style/Widget.Quantum.Light.ProgressBar.Small.Inverse</item>
- <item name="progressBarStyleLargeInverse">@style/Widget.Quantum.Light.ProgressBar.Large.Inverse</item>
- <item name="seekBarStyle">@style/Widget.Quantum.Light.SeekBar</item>
- <item name="ratingBarStyle">@style/Widget.Quantum.Light.RatingBar</item>
- <item name="ratingBarStyleIndicator">@style/Widget.Quantum.Light.RatingBar.Indicator</item>
- <item name="ratingBarStyleSmall">@style/Widget.Quantum.Light.RatingBar.Small</item>
- <item name="radioButtonStyle">@style/Widget.Quantum.Light.CompoundButton.RadioButton</item>
- <item name="scrollViewStyle">@style/Widget.Quantum.Light.ScrollView</item>
- <item name="horizontalScrollViewStyle">@style/Widget.Quantum.Light.HorizontalScrollView</item>
- <item name="spinnerStyle">?attr/dropDownSpinnerStyle</item>
- <item name="dropDownSpinnerStyle">@style/Widget.Quantum.Light.Spinner.DropDown</item>
- <item name="starStyle">@style/Widget.Quantum.Light.CompoundButton.Star</item>
- <item name="tabWidgetStyle">@style/Widget.Quantum.Light.TabWidget</item>
- <item name="textViewStyle">@style/Widget.Quantum.Light.TextView</item>
- <item name="errorMessageBackground">@drawable/popup_inline_error_holo_light</item>
- <item name="errorMessageAboveBackground">@drawable/popup_inline_error_above_holo_light</item>
- <item name="webTextViewStyle">@style/Widget.Quantum.Light.WebTextView</item>
- <item name="webViewStyle">@style/Widget.Quantum.Light.WebView</item>
- <item name="dropDownItemStyle">@style/Widget.Quantum.Light.DropDownItem</item>
- <item name="spinnerDropDownItemStyle">@style/Widget.Quantum.Light.DropDownItem.Spinner</item>
- <item name="spinnerItemStyle">@style/Widget.Quantum.TextView.SpinnerItem</item>
- <item name="dropDownHintAppearance">@style/TextAppearance.Quantum.Widget.DropDownHint</item>
- <item name="keyboardViewStyle">@style/Widget.Quantum.KeyboardView</item>
- <item name="quickContactBadgeStyleWindowSmall">@style/Widget.Quantum.QuickContactBadge.WindowSmall</item>
- <item name="quickContactBadgeStyleWindowMedium">@style/Widget.Quantum.QuickContactBadge.WindowMedium</item>
- <item name="quickContactBadgeStyleWindowLarge">@style/Widget.Quantum.QuickContactBadge.WindowLarge</item>
- <item name="quickContactBadgeStyleSmallWindowSmall">@style/Widget.Quantum.QuickContactBadgeSmall.WindowSmall</item>
- <item name="quickContactBadgeStyleSmallWindowMedium">@style/Widget.Quantum.QuickContactBadgeSmall.WindowMedium</item>
- <item name="quickContactBadgeStyleSmallWindowLarge">@style/Widget.Quantum.QuickContactBadgeSmall.WindowLarge</item>
- <item name="listPopupWindowStyle">@style/Widget.Quantum.Light.ListPopupWindow</item>
- <item name="popupMenuStyle">@style/Widget.Quantum.Light.PopupMenu</item>
- <item name="stackViewStyle">@style/Widget.Quantum.Light.StackView</item>
- <item name="activityChooserViewStyle">@style/Widget.Quantum.Light.ActivityChooserView</item>
-
- <!-- Preference styles -->
- <item name="preferenceScreenStyle">@style/Preference.Quantum.PreferenceScreen</item>
- <item name="preferenceFragmentStyle">@style/PreferenceFragment.Quantum</item>
- <item name="preferenceFragmentPaddingSide">0dip</item>
- <item name="preferenceCategoryStyle">@style/Preference.Quantum.Category</item>
- <item name="preferenceStyle">@style/Preference.Quantum</item>
- <item name="preferenceInformationStyle">@style/Preference.Quantum.Information</item>
- <item name="checkBoxPreferenceStyle">@style/Preference.Quantum.CheckBoxPreference</item>
- <item name="switchPreferenceStyle">@style/Preference.Quantum.SwitchPreference</item>
- <item name="yesNoPreferenceStyle">@style/Preference.Quantum.DialogPreference.YesNoPreference</item>
- <item name="dialogPreferenceStyle">@style/Preference.Quantum.DialogPreference</item>
- <item name="editTextPreferenceStyle">@style/Preference.Quantum.DialogPreference.EditTextPreference</item>
- <item name="ringtonePreferenceStyle">@style/Preference.Quantum.RingtonePreference</item>
- <item name="preferenceLayoutChild">@layout/preference_child_quantum</item>
- <item name="detailsElementBackground">?attr/colorBackground</item>
-
- <!-- PreferenceFrameLayout attributes -->
- <item name="preferenceFrameLayoutStyle">@style/Widget.Quantum.PreferenceFrameLayout</item>
-
- <!-- Search widget styles -->
- <item name="searchWidgetCorpusItemBackground">@color/search_widget_corpus_item_background</item>
-
- <!-- Action bar styles -->
- <item name="actionDropDownStyle">@style/Widget.Quantum.Light.Spinner.DropDown.ActionBar</item>
- <item name="actionButtonStyle">@style/Widget.Quantum.Light.ActionButton</item>
- <item name="actionOverflowButtonStyle">@style/Widget.Quantum.Light.ActionButton.Overflow</item>
- <item name="actionOverflowMenuStyle">@android:style/Widget.Quantum.Light.PopupMenu.Overflow</item>
- <item name="actionModeBackground">@drawable/cab_background_top_holo_light</item>
- <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
- <item name="actionModeCloseDrawable">@drawable/ic_cab_done_quantum</item>
- <item name="actionBarTabStyle">@style/Widget.Quantum.Light.ActionBar.TabView</item>
- <item name="actionBarTabBarStyle">@style/Widget.Quantum.Light.ActionBar.TabBar</item>
- <item name="actionBarTabTextStyle">@style/Widget.Quantum.Light.ActionBar.TabText</item>
- <item name="actionModeStyle">@style/Widget.Quantum.Light.ActionMode</item>
- <item name="actionModeCloseButtonStyle">@style/Widget.Quantum.Light.ActionButton.CloseMode</item>
- <item name="actionBarStyle">@style/Widget.Quantum.Light.ActionBar.Solid</item>
- <item name="actionBarSize">@dimen/action_bar_default_height_quantum</item>
- <item name="actionModePopupWindowStyle">@style/Widget.Quantum.Light.PopupWindow.ActionMode</item>
- <item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item>
- <item name="actionBarTheme">@null</item>
- <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
-
- <item name="actionModeCutDrawable">@drawable/ic_menu_cut_quantum</item>
- <item name="actionModeCopyDrawable">@drawable/ic_menu_copy_quantum</item>
- <item name="actionModePasteDrawable">@drawable/ic_menu_paste_quantum</item>
- <item name="actionModeSelectAllDrawable">@drawable/ic_menu_selectall_quantum</item>
- <item name="actionModeShareDrawable">@drawable/ic_menu_share_quantum</item>
- <item name="actionModeFindDrawable">@drawable/ic_menu_find_quantum</item>
- <item name="actionModeWebSearchDrawable">@drawable/ic_menu_search_quantum</item>
-
- <item name="dividerVertical">?attr/listDivider</item>
- <item name="dividerHorizontal">?attr/listDivider</item>
- <item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar</item>
- <item name="buttonBarButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
- <item name="segmentedButtonStyle">@style/Widget.Quantum.Light.SegmentedButton</item>
-
- <!-- SearchView attributes -->
- <item name="searchDropdownBackground">?attr/colorBackground</item>
- <item name="searchViewTextField">@drawable/textfield_search_quantum</item>
- <item name="searchViewTextFieldRight">@drawable/textfield_search_quantum</item>
- <item name="searchViewCloseIcon">@android:drawable/ic_clear_quantum</item>
- <item name="searchViewSearchIcon">@android:drawable/ic_search_api_quantum</item>
- <item name="searchViewGoIcon">@android:drawable/ic_go_search_api_quantum</item>
- <item name="searchViewVoiceIcon">@android:drawable/ic_voice_search_api_quantum</item>
- <item name="searchViewEditQuery">@android:drawable/ic_commit_search_api_quantum</item>
-
- <item name="searchDialogTheme">@style/Theme.Quantum.Light.SearchBar</item>
-
- <!-- NumberPicker style-->
- <item name="numberPickerStyle">@style/Widget.Quantum.Light.NumberPicker</item>
-
- <!-- CalendarView style-->
- <item name="calendarViewStyle">@style/Widget.Quantum.Light.CalendarView</item>
-
- <!-- TimePicker style -->
- <item name="timePickerStyle">@style/Widget.Quantum.Light.TimePicker</item>
-
- <!-- TimePicker Header background color -->
- <item name="timePickerHeaderBackgroundColor">?attr/colorBackground</item>
-
- <!-- TimePicker Header time label text appearance -->
- <item name="timePickerHeaderTimeLabelTextAppearance">@style/TextAppearance.Quantum.TimePicker.TimeLabel</item>
-
- <!-- TimePicker Header am pm label text appearance -->
- <item name="timePickerHeaderAmPmLabelTextAppearance">@style/TextAppearance.Quantum.TimePicker.AmPmLabel</item>
-
- <!-- TimePicker dialog theme -->
- <item name="timePickerDialogTheme">@style/Theme.Quantum.Light.Dialog.TimePicker</item>
-
- <!-- DatePicker style -->
- <item name="datePickerStyle">@style/Widget.Quantum.Light.DatePicker</item>
-
- <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_quantum</item>
- <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
- <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
- <item name="fastScrollTrackDrawable">@drawable/fastscroll_track_quantum</item>
- <item name="fastScrollOverlayPosition">atThumb</item>
-
- <!-- Color palette -->
- <item name="colorPrimaryDark">@color/quantum_blue_700</item>
- <item name="colorPrimary">@color/quantum_blue_500</item>
- <item name="colorPrimaryLight">@color/quantum_blue_100</item>
- <item name="colorAccent">@color/quantum_teal_A200</item>
-
- <item name="colorControlNormal">?attr/textColorSecondary</item>
- <item name="colorControlActivated">?attr/colorPrimary</item>
-
- <item name="colorControlHighlight">@color/ripple_quantum_light</item>
- <item name="colorButtonNormal">@color/btn_default_quantum_light</item>
- </style>
-
- <!-- Variant of the quantum (light) theme that has a solid (opaque) action bar
- with an inverse color profile. The dark action bar sharply stands out against
- the light content. -->
- <style name="Theme.Quantum.Light.DarkActionBar">
- <item name="actionBarWidgetTheme">@style/ThemeOverlay.Quantum.ActionBarWidget</item>
- <item name="actionBarTheme">@style/ThemeOverlay.Quantum.Dark</item>
- </style>
-
- <style name="ThemeOverlay" />
- <style name="ThemeOverlay.Quantum" />
-
- <!-- Theme overlay that replaces colors with their light versions but preserves
- the value of colorAccent, colorPrimary and its variants. -->
- <style name="ThemeOverlay.Quantum.Light">
- <item name="colorForeground">@color/bright_foreground_quantum_light</item>
- <item name="colorForegroundInverse">@color/bright_foreground_quantum_dark</item>
- <item name="colorBackground">@color/background_quantum_light</item>
- <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_quantum_light</item>
-
- <item name="textColorPrimary">@color/primary_text_quantum_light</item>
- <item name="textColorPrimaryInverse">@color/primary_text_quantum_dark</item>
- <item name="textColorSecondary">@color/secondary_text_quantum_light</item>
- <item name="textColorSecondaryInverse">@color/secondary_text_quantum_dark</item>
- <item name="textColorTertiary">@color/tertiary_text_quantum_light</item>
- <item name="textColorTertiaryInverse">@color/tertiary_text_quantum_dark</item>
- <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_quantum_light</item>
- <item name="textColorPrimaryInverseDisableOnly">@color/primary_text_disable_only_quantum_dark</item>
- <item name="textColorHint">@color/hint_foreground_quantum_light</item>
- <item name="textColorHintInverse">@color/hint_foreground_quantum_dark</item>
- <item name="textColorHighlight">@color/highlighted_text_quantum_light</item>
- <item name="textColorHighlightInverse">@color/highlighted_text_quantum_dark</item>
- <item name="textColorLink">@color/quantum_teal_500</item>
- <item name="textColorLinkInverse">@color/quantum_teal_500</item>
- <item name="textColorSearchUrl">@color/search_url_text_quantum_light</item>
- <item name="textColorAlertDialogListItem">@color/primary_text_quantum_light</item>
-
- <item name="textCheckMark">@drawable/indicator_check_mark_light</item>
- <item name="textCheckMarkInverse">@drawable/indicator_check_mark_dark</item>
-
- <item name="windowBackground">@color/background_quantum_light</item>
-
- <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
- <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
-
- <item name="colorControlHighlight">@color/ripple_quantum_light</item>
- <item name="colorButtonNormal">@color/btn_default_quantum_light</item>
- </style>
-
- <!-- Theme overlay that replaces colors with their dark versions but preserves
- the value of colorAccent, colorPrimary and its variants. -->
- <style name="ThemeOverlay.Quantum.Dark">
- <item name="colorForeground">@color/bright_foreground_quantum_dark</item>
- <item name="colorForegroundInverse">@color/bright_foreground_quantum_light</item>
- <item name="colorBackground">@color/background_quantum_dark</item>
- <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_quantum_dark</item>
-
- <item name="textColorPrimary">@color/primary_text_quantum_dark</item>
- <item name="textColorPrimaryInverse">@color/primary_text_quantum_light</item>
- <item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_quantum_dark</item>
- <item name="textColorSecondary">@color/secondary_text_quantum_dark</item>
- <item name="textColorSecondaryInverse">@color/secondary_text_quantum_light</item>
- <item name="textColorTertiary">@color/tertiary_text_quantum_dark</item>
- <item name="textColorTertiaryInverse">@color/tertiary_text_quantum_light</item>
- <item name="textColorHint">@color/hint_foreground_quantum_dark</item>
- <item name="textColorHintInverse">@color/hint_foreground_quantum_light</item>
- <item name="textColorHighlight">@color/highlighted_text_quantum_dark</item>
- <item name="textColorHighlightInverse">@color/highlighted_text_quantum_light</item>
- <item name="textColorLink">@color/quantum_teal_500</item>
- <item name="textColorLinkInverse">@color/quantum_teal_500</item>
- <item name="textColorSearchUrl">@color/search_url_text_quantum_dark</item>
- <item name="textColorAlertDialogListItem">@color/primary_text_quantum_dark</item>
-
- <item name="textCheckMark">@drawable/indicator_check_mark_dark</item>
- <item name="textCheckMarkInverse">@drawable/indicator_check_mark_light</item>
-
- <item name="windowBackground">@color/background_quantum_dark</item>
-
- <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
- <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
-
- <item name="colorControlHighlight">@color/ripple_quantum_dark</item>
- <item name="colorButtonNormal">@color/btn_default_quantum_dark</item>
- </style>
-
- <!-- Theme overlay that replaces the activated control color (which by default
- is identical to the action bar background color) with the normal control
- color. -->
- <style name="ThemeOverlay.Quantum.ActionBarWidget">
- <item name="colorControlActivated">?attr/colorControlNormal</item>
- </style>
-
- <!-- Variant of the quantum (dark) theme with no action bar. -->
- <style name="Theme.Quantum.NoActionBar">
- <item name="windowActionBar">false</item>
- <item name="windowNoTitle">true</item>
- </style>
-
- <!-- Variant of the quantum (dark) theme that has no title bar and fills
- the entire screen. This theme
- sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.Quantum.NoActionBar.Fullscreen">
- <item name="windowFullscreen">true</item>
- <item name="windowContentOverlay">@null</item>
- </style>
-
- <!-- Variant of the quantum (dark) theme that has no title bar and fills
- the entire screen and extends into the display overscan region. This theme
- sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
- to true. -->
- <style name="Theme.Quantum.NoActionBar.Overscan">
- <item name="windowFullscreen">true</item>
- <item name="windowOverscan">true</item>
- <item name="windowContentOverlay">@null</item>
- </style>
-
- <!-- Variant of the quantum (dark) theme that has no title bar and translucent
- system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
- {@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.Quantum.NoActionBar.TranslucentDecor">
- <item name="windowTranslucentStatus">true</item>
- <item name="windowTranslucentNavigation">true</item>
- <item name="windowContentOverlay">@null</item>
- </style>
-
- <!-- Variant of the quantum (light) theme with no action bar. -->
- <style name="Theme.Quantum.Light.NoActionBar">
- <item name="windowActionBar">false</item>
- <item name="windowNoTitle">true</item>
- </style>
-
- <!-- Variant of the quantum (light) theme that has no title bar and fills
- the entire screen. This theme
- sets {@link android.R.attr#windowFullscreen} to true. -->
- <style name="Theme.Quantum.Light.NoActionBar.Fullscreen">
- <item name="windowFullscreen">true</item>
- <item name="windowContentOverlay">@null</item>
- </style>
-
- <!-- Variant of the quantum (light) theme that has no title bar and fills
- the entire screen and extends into the display overscan region. This theme
- sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
- to true. -->
- <style name="Theme.Quantum.Light.NoActionBar.Overscan">
- <item name="windowFullscreen">true</item>
- <item name="windowOverscan">true</item>
- <item name="windowContentOverlay">@null</item>
- </style>
-
- <!-- Variant of the quantum (light) theme that has no title bar and translucent
- system decor. This theme sets {@link android.R.attr#windowTranslucentStatus} and
- {@link android.R.attr#windowTranslucentNavigation} to true. -->
- <style name="Theme.Quantum.Light.NoActionBar.TranslucentDecor">
- <item name="windowTranslucentStatus">true</item>
- <item name="windowTranslucentNavigation">true</item>
- <item name="windowContentOverlay">@null</item>
- </style>
-
- <!-- Default quantum dark theme for panel windows. This removes all extraneous
- window decorations, so you basically have an empty rectangle in which
- to place your content. It makes the window floating, with a transparent
- background, and turns off dimming behind the window. -->
- <style name="Theme.Quantum.Panel">
- <item name="windowBackground">@color/transparent</item>
- <item name="colorBackgroundCacheHint">@null</item>
- <item name="windowFrame">@null</item>
- <item name="windowContentOverlay">@null</item>
- <item name="windowAnimationStyle">@null</item>
- <item name="windowIsFloating">true</item>
- <item name="backgroundDimEnabled">false</item>
- <item name="windowIsTranslucent">true</item>
- <item name="windowNoTitle">true</item>
- </style>
-
- <!-- Default quantum light theme for panel windows. This removes all extraneous
- window decorations, so you basically have an empty rectangle in which
- to place your content. It makes the window floating, with a transparent
- background, and turns off dimming behind the window. -->
- <style name="Theme.Quantum.Light.Panel">
- <item name="windowBackground">@color/transparent</item>
- <item name="colorBackgroundCacheHint">@null</item>
- <item name="windowFrame">@null</item>
- <item name="windowContentOverlay">@null</item>
- <item name="windowAnimationStyle">@null</item>
- <item name="windowIsFloating">true</item>
- <item name="backgroundDimEnabled">false</item>
- <item name="windowIsTranslucent">true</item>
- <item name="windowNoTitle">true</item>
- </style>
-
- <!-- Quantum theme for an activity that is to be used for voice interaction.
- This gives the activity a floating dialog style, to incorporate with the
- system voice experience. -->
- <style name="Theme.Quantum.Voice" parent="@style/Theme.Quantum.Dialog">
- <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
- <item name="backgroundDimEnabled">false</item>
- </style>
-
- <!-- Quantum light theme for an activity that is to be used for voice interaction.
- This gives the activity a floating dialog style, to incorporate with the
- system voice experience. -->
- <style name="Theme.Quantum.Light.Voice" parent="@style/Theme.Quantum.Light.Dialog">
- <item name="windowAnimationStyle">@style/Animation.VoiceActivity</item>
- <item name="backgroundDimEnabled">false</item>
- </style>
-
- <!-- Default theme for quantum style input methods, which is used by the
- {@link android.inputmethodservice.InputMethodService} class.
- this inherits from Theme.Panel, but sets up IME appropriate animations
- and a few custom attributes. -->
- <style name="Theme.Quantum.InputMethod" parent="Theme.Quantum.Light.Panel">
- <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
- <item name="imeFullscreenBackground">@drawable/screen_background_selector_light</item>
- <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
- <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
- </style>
-
- <!-- Default theme for quantum style voice interaction, which is used by the
- {@link android.service.voice.VoiceInteractionSession} class.
- this inherits from Theme.Panel, but sets up appropriate animations
- and a few custom attributes. -->
- <style name="Theme.Quantum.VoiceInteractionSession" parent="Theme.Quantum.Light.Panel">
- <item name="windowAnimationStyle">@style/Animation.VoiceInteractionSession</item>
- </style>
-
- <!-- Theme for the search input bar. -->
-
- <style name="Theme.Quantum.SearchBar" parent="Theme.Quantum.Panel">
- <item name="actionModeBackground">@drawable/cab_background_top_holo_dark</item>
- <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
- </style>
-
- <style name="Theme.Quantum.Light.SearchBar" parent="Theme.Quantum.Light.Panel">
- <item name="actionModeBackground">@drawable/cab_background_top_holo_light</item>
- <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
- </style>
-
- <!-- Menu Themes -->
- <eat-comment />
-
- <style name="Theme.Quantum.CompactMenu">
- <!-- Menu/item attributes -->
- <item name="itemTextAppearance">?attr/textAppearanceMedium</item>
- <item name="listViewStyle">@style/Widget.Quantum.ListView</item>
- <item name="windowAnimationStyle">@style/Animation.DropDownUp</item>
- <item name="background">@null</item>
- </style>
-
- <style name="Theme.Quantum.Light.CompactMenu">
- <!-- Menu/item attributes -->
- <item name="itemTextAppearance">?attr/textAppearanceMedium</item>
- <item name="listViewStyle">@style/Widget.Quantum.Light.ListView</item>
- <item name="windowAnimationStyle">@style/Animation.DropDownUp</item>
- <item name="background">@null</item>
- </style>
-
- <!-- Dialog themes for Quantum -->
- <eat-comment />
-
- <!-- Quantum theme for dialog windows and activities, which is used by the
- {@link android.app.Dialog} class. This changes the window to be
- floating (not fill the entire screen), and puts a frame around its
- contents. You can set this theme on an activity if you would like to
- make an activity that looks like a Dialog. -->
- <style name="Theme.Quantum.Dialog">
- <item name="windowFrame">@null</item>
- <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum</item>
- <item name="windowBackground">@drawable/dialog_background_quantum</item>
- <item name="windowIsFloating">true</item>
- <item name="windowContentOverlay">@null</item>
- <item name="windowAnimationStyle">@style/Animation.Quantum.Dialog</item>
- <item name="windowSoftInputMode">stateUnspecified|adjustPan</item>
- <item name="windowActionBar">false</item>
- <item name="windowActionModeOverlay">true</item>
- <item name="windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
-
- <item name="colorBackgroundCacheHint">@null</item>
-
- <item name="buttonBarStyle">@style/Widget.Quantum.ButtonBar.AlertDialog</item>
- <item name="borderlessButtonStyle">@style/Widget.Quantum.Button.Borderless</item>
-
- <item name="textAppearance">@style/TextAppearance.Quantum</item>
- <item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item>
-
- <item name="listPreferredItemPaddingLeft">16dip</item>
- <item name="listPreferredItemPaddingRight">16dip</item>
- <item name="listPreferredItemPaddingStart">16dip</item>
- <item name="listPreferredItemPaddingEnd">16dip</item>
-
- <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Dialog that has a nice minimum width for
- a regular dialog. -->
- <style name="Theme.Quantum.Dialog.MinWidth">
- <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
- <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Dialog that does not include a title bar. -->
- <style name="Theme.Quantum.Dialog.NoActionBar">
- <item name="windowActionBar">false</item>
- <item name="windowNoTitle">true</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Dialog.NoActionBar that has a nice minimum width for
- a regular dialog. -->
- <style name="Theme.Quantum.Dialog.NoActionBar.MinWidth">
- <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
- <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Dialog that has a fixed size. -->
- <style name="Theme.Quantum.Dialog.FixedSize">
- <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
- <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
- <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
- <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Dialog.NoActionBar that has a fixed size. -->
- <style name="Theme.Quantum.Dialog.NoActionBar.FixedSize">
- <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
- <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
- <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
- <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Dialog that does not include a frame (or background).
- The view hierarchy of the dialog is responsible for drawing all of
- its pixels. -->
- <style name="Theme.Quantum.Dialog.NoFrame">
- <item name="windowBackground">@color/transparent</item>
- <item name="windowAnimationStyle">@null</item>
- <item name="backgroundDimEnabled">false</item>
- <item name="windowIsTranslucent">true</item>
- <item name="windowNoTitle">true</item>
- <item name="windowCloseOnTouchOutside">false</item>
- </style>
-
- <!-- Quantum theme for alert dialog windows, which is used by the
- {@link android.app.AlertDialog} class. This is basically a dialog
- but sets the background to empty so it can do two-tone backgrounds.
- For applications targeting Honeycomb or newer, this is the default
- AlertDialog theme. -->
- <style name="Theme.Quantum.Dialog.Alert">
- <item name="windowBackground">@color/transparent</item>
- <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum</item>
- <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
- <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
- </style>
-
- <!-- Quantum theme for the TimePicker dialog windows, which is used by the
- {@link android.app.TimePickerDialog} class. -->
- <style name="Theme.Quantum.Dialog.TimePicker">
- <item name="windowBackground">@color/transparent</item>
- <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum</item>
- <item name="windowContentOverlay">@null</item>
- </style>
-
- <!-- Theme for a window that will be displayed either full-screen on
- smaller screens (small, normal) or as a dialog on larger screens
- (large, xlarge). -->
- <style name="Theme.Quantum.DialogWhenLarge" parent="@style/Theme.Quantum">
- </style>
-
- <!-- Theme for a window without a title bar that will be displayed either
- full-screen on smaller screens (small, normal) or as a dialog on larger screens
- (large, xlarge). -->
- <style name="Theme.Quantum.DialogWhenLarge.NoActionBar" parent="@style/Theme.Quantum.NoActionBar">
- </style>
-
- <!-- Theme for a presentation window on a secondary display. -->
- <style name="Theme.Quantum.Dialog.Presentation" parent="@style/Theme.Quantum.NoActionBar.Fullscreen">
- </style>
-
- <!-- Light quantum dialog themes -->
-
- <!-- Quantum light theme for dialog windows and activities, which is used by the
- {@link android.app.Dialog} class. This changes the window to be
- floating (not fill the entire screen), and puts a frame around its
- contents. You can set this theme on an activity if you would like to
- make an activity that looks like a Dialog. -->
- <style name="Theme.Quantum.Light.Dialog">
- <item name="windowFrame">@null</item>
- <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum.Light</item>
- <item name="windowBackground">?attr/colorBackground</item>
- <item name="windowIsFloating">true</item>
- <item name="windowContentOverlay">@null</item>
- <item name="windowAnimationStyle">@style/Animation.Quantum.Dialog</item>
- <item name="windowSoftInputMode">stateUnspecified|adjustPan</item>
- <item name="windowActionBar">false</item>
- <item name="windowActionModeOverlay">true</item>
- <item name="windowCloseOnTouchOutside">@bool/config_closeDialogWhenTouchOutside</item>
-
- <item name="colorBackgroundCacheHint">@null</item>
-
- <item name="buttonBarStyle">@style/Widget.Quantum.Light.ButtonBar.AlertDialog</item>
- <item name="borderlessButtonStyle">@style/Widget.Quantum.Light.Button.Borderless</item>
-
- <item name="textAppearance">@style/TextAppearance.Quantum</item>
- <item name="textAppearanceInverse">@style/TextAppearance.Quantum.Inverse</item>
-
- <item name="listPreferredItemPaddingLeft">16dip</item>
- <item name="listPreferredItemPaddingRight">16dip</item>
- <item name="listPreferredItemPaddingStart">16dip</item>
- <item name="listPreferredItemPaddingEnd">16dip</item>
-
- <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Light.Dialog that has a nice minimum width for
- a regular dialog. -->
- <style name="Theme.Quantum.Light.Dialog.MinWidth">
- <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
- <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Light.Dialog that does not include a title bar. -->
- <style name="Theme.Quantum.Light.Dialog.NoActionBar">
- <item name="windowActionBar">false</item>
- <item name="windowNoTitle">true</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Light.Dialog.NoActionBar that has a nice minimum width for
- a regular dialog. -->
- <style name="Theme.Quantum.Light.Dialog.NoActionBar.MinWidth">
- <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
- <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Light.Dialog that has a fixed size. -->
- <style name="Theme.Quantum.Light.Dialog.FixedSize">
- <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
- <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
- <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
- <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
- </style>
-
- <!-- Variant of Theme.Quantum.Light.Dialog.NoActionBar that has a fixed size. -->
- <style name="Theme.Quantum.Light.Dialog.NoActionBar.FixedSize">
- <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
- <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
- <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
- <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
- </style>
-
- <!-- Theme for a window that will be displayed either full-screen on
- smaller screens (small, normal) or as a dialog on larger screens
- (large, xlarge). -->
- <style name="Theme.Quantum.Light.DialogWhenLarge" parent="@style/Theme.Quantum.Light">
- </style>
-
- <!-- Theme for a window without an action bar that will be displayed either full-screen
- on smaller screens (small, normal) or as a dialog on larger screens
- (large, xlarge). -->
- <style name="Theme.Quantum.Light.DialogWhenLarge.NoActionBar"
- parent="@style/Theme.Quantum.Light.NoActionBar">
- </style>
-
- <!-- Quantum light theme for alert dialog windows, which is used by the
- {@link android.app.AlertDialog} class. This is basically a dialog
- but sets the background to empty so it can do two-tone backgrounds.
- For applications targeting Honeycomb or newer, this is the default
- AlertDialog theme. -->
- <style name="Theme.Quantum.Light.Dialog.Alert">
- <item name="windowBackground">@color/transparent</item>
- <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum.Light</item>
- <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
- <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
- </style>
-
- <!-- Quantum Light theme for the TimePicker dialog windows, which is used by the
- {@link android.app.TimePickerDialog} class. -->
- <style name="Theme.Quantum.Light.Dialog.TimePicker">
- <item name="windowBackground">@color/transparent</item>
- <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum.Light</item>
- </style>
-
- <!-- Theme for a presentation window on a secondary display. -->
- <style name="Theme.Quantum.Light.Dialog.Presentation" parent="@style/Theme.Quantum.Light.NoActionBar.Fullscreen" >
- </style>
-
- <!-- Default quantum (dark) for windows that want to have the user's selected
- wallpaper appear behind them. -->
- <style name="Theme.Quantum.Wallpaper">
- <item name="windowBackground">@color/transparent</item>
- <item name="colorBackgroundCacheHint">@null</item>
- <item name="windowShowWallpaper">true</item>
- </style>
-
- <!--Default quantum (dark) for windows that want to have the user's selected
- wallpaper appear behind them and without an action bar. -->
- <style name="Theme.Quantum.Wallpaper.NoTitleBar">
- <item name="windowNoTitle">true</item>
- </style>
-
-</resources>
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
index db125e6..116a31e 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/AccessPointParserHelper.java
@@ -336,7 +336,7 @@
if (!InetAddress.isNumeric(dnsAddr)) {
throw new SAXException();
}
- mLinkProperties.addDns(InetAddress.getByName(dnsAddr));
+ mLinkProperties.addDnsServer(InetAddress.getByName(dnsAddr));
} catch (UnknownHostException e) {
throw new SAXException();
}
@@ -348,7 +348,7 @@
if (!InetAddress.isNumeric(dnsAddr)) {
throw new SAXException();
}
- mLinkProperties.addDns(InetAddress.getByName(dnsAddr));
+ mLinkProperties.addDnsServer(InetAddress.getByName(dnsAddr));
} catch (UnknownHostException e) {
throw new SAXException();
}
diff --git a/core/tests/coretests/src/android/net/LinkAddressTest.java b/core/tests/coretests/src/android/net/LinkAddressTest.java
index bccf556..7bc3974 100644
--- a/core/tests/coretests/src/android/net/LinkAddressTest.java
+++ b/core/tests/coretests/src/android/net/LinkAddressTest.java
@@ -30,6 +30,7 @@
import android.net.LinkAddress;
import android.os.Parcel;
import android.test.AndroidTestCase;
+import static android.test.MoreAsserts.assertNotEqual;
import android.test.suitebuilder.annotation.SmallTest;
import static android.system.OsConstants.IFA_F_DEPRECATED;
@@ -50,32 +51,43 @@
private static final InetAddress V4_ADDRESS = NetworkUtils.numericToInetAddress(V4);
private static final InetAddress V6_ADDRESS = NetworkUtils.numericToInetAddress(V6);
+ public void testConstants() {
+ // RT_SCOPE_UNIVERSE = 0, but all the other constants should be nonzero.
+ assertNotEqual(0, RT_SCOPE_HOST);
+ assertNotEqual(0, RT_SCOPE_LINK);
+ assertNotEqual(0, RT_SCOPE_SITE);
+
+ assertNotEqual(0, IFA_F_DEPRECATED);
+ assertNotEqual(0, IFA_F_PERMANENT);
+ assertNotEqual(0, IFA_F_TENTATIVE);
+ }
+
public void testConstructors() throws SocketException {
LinkAddress address;
// Valid addresses work as expected.
address = new LinkAddress(V4_ADDRESS, 25);
assertEquals(V4_ADDRESS, address.getAddress());
- assertEquals(25, address.getNetworkPrefixLength());
+ assertEquals(25, address.getPrefixLength());
assertEquals(0, address.getFlags());
assertEquals(RT_SCOPE_UNIVERSE, address.getScope());
address = new LinkAddress(V6_ADDRESS, 127);
assertEquals(V6_ADDRESS, address.getAddress());
- assertEquals(127, address.getNetworkPrefixLength());
+ assertEquals(127, address.getPrefixLength());
assertEquals(0, address.getFlags());
assertEquals(RT_SCOPE_UNIVERSE, address.getScope());
// Nonsensical flags/scopes or combinations thereof are acceptable.
address = new LinkAddress(V6 + "/64", IFA_F_DEPRECATED | IFA_F_PERMANENT, RT_SCOPE_LINK);
assertEquals(V6_ADDRESS, address.getAddress());
- assertEquals(64, address.getNetworkPrefixLength());
+ assertEquals(64, address.getPrefixLength());
assertEquals(IFA_F_DEPRECATED | IFA_F_PERMANENT, address.getFlags());
assertEquals(RT_SCOPE_LINK, address.getScope());
address = new LinkAddress(V4 + "/23", 123, 456);
assertEquals(V4_ADDRESS, address.getAddress());
- assertEquals(23, address.getNetworkPrefixLength());
+ assertEquals(23, address.getPrefixLength());
assertEquals(123, address.getFlags());
assertEquals(456, address.getScope());
@@ -94,10 +106,10 @@
}
assertEquals(NetworkUtils.numericToInetAddress("127.0.0.1"), ipv4Loopback.getAddress());
- assertEquals(8, ipv4Loopback.getNetworkPrefixLength());
+ assertEquals(8, ipv4Loopback.getPrefixLength());
assertEquals(NetworkUtils.numericToInetAddress("::1"), ipv6Loopback.getAddress());
- assertEquals(128, ipv6Loopback.getNetworkPrefixLength());
+ assertEquals(128, ipv6Loopback.getPrefixLength());
// Null addresses are rejected.
try {
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index 553afe0..e649baa 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -88,8 +88,8 @@
source.addLinkAddress(LINKADDRV4);
source.addLinkAddress(LINKADDRV6);
// set 2 dnses
- source.addDns(DNS1);
- source.addDns(DNS2);
+ source.addDnsServer(DNS1);
+ source.addDnsServer(DNS2);
// set 2 gateways
source.addRoute(new RouteInfo(GATEWAY1));
source.addRoute(new RouteInfo(GATEWAY2));
@@ -101,8 +101,8 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -114,8 +114,8 @@
target.setInterfaceName("qmi1");
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -127,8 +127,8 @@
target.addLinkAddress(new LinkAddress(
NetworkUtils.numericToInetAddress("75.208.6.2"), 32));
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -139,8 +139,8 @@
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
// change dnses
- target.addDns(NetworkUtils.numericToInetAddress("75.208.7.2"));
- target.addDns(DNS2);
+ target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -150,8 +150,8 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
// change gateway
target.addRoute(new RouteInfo(NetworkUtils.numericToInetAddress("75.208.8.2")));
target.addRoute(new RouteInfo(GATEWAY2));
@@ -162,8 +162,8 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV4);
target.addLinkAddress(LINKADDRV6);
- target.addDns(DNS1);
- target.addDns(DNS2);
+ target.addDnsServer(DNS1);
+ target.addDnsServer(DNS2);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
// change mtu
@@ -185,8 +185,8 @@
source.addLinkAddress(LINKADDRV4);
source.addLinkAddress(LINKADDRV6);
// set 2 dnses
- source.addDns(DNS1);
- source.addDns(DNS2);
+ source.addDnsServer(DNS1);
+ source.addDnsServer(DNS2);
// set 2 gateways
source.addRoute(new RouteInfo(GATEWAY1));
source.addRoute(new RouteInfo(GATEWAY2));
@@ -197,8 +197,8 @@
target.setInterfaceName(NAME);
target.addLinkAddress(LINKADDRV6);
target.addLinkAddress(LINKADDRV4);
- target.addDns(DNS2);
- target.addDns(DNS1);
+ target.addDnsServer(DNS2);
+ target.addDnsServer(DNS1);
target.addRoute(new RouteInfo(GATEWAY2));
target.addRoute(new RouteInfo(GATEWAY1));
target.setMtu(MTU);
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java
index 55d6592..c80d0bf 100644
--- a/core/tests/coretests/src/android/net/RouteInfoTest.java
+++ b/core/tests/coretests/src/android/net/RouteInfoTest.java
@@ -43,17 +43,17 @@
// Invalid input.
try {
- r = new RouteInfo(null, null, "rmnet0");
+ r = new RouteInfo((LinkAddress) null, null, "rmnet0");
fail("Expected RuntimeException: destination and gateway null");
} catch(RuntimeException e) {}
// Null destination is default route.
- r = new RouteInfo(null, Address("2001:db8::1"), null);
+ r = new RouteInfo((LinkAddress) null, Address("2001:db8::1"), null);
assertEquals(Prefix("::/0"), r.getDestination());
assertEquals(Address("2001:db8::1"), r.getGateway());
assertNull(r.getInterface());
- r = new RouteInfo(null, Address("192.0.2.1"), "wlan0");
+ r = new RouteInfo((LinkAddress) null, Address("192.0.2.1"), "wlan0");
assertEquals(Prefix("0.0.0.0/0"), r.getDestination());
assertEquals(Address("192.0.2.1"), r.getGateway());
assertEquals("wlan0", r.getInterface());
@@ -71,17 +71,19 @@
}
public void testMatches() {
- class PatchedRouteInfo extends RouteInfo {
+ class PatchedRouteInfo {
+ private final RouteInfo mRouteInfo;
+
public PatchedRouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
- super(destination, gateway, iface);
+ mRouteInfo = new RouteInfo(destination, gateway, iface);
}
public boolean matches(InetAddress destination) {
- return super.matches(destination);
+ return mRouteInfo.matches(destination);
}
}
- RouteInfo r;
+ PatchedRouteInfo r;
r = new PatchedRouteInfo(Prefix("2001:db8:f00::ace:d00d/127"), null, "rmnet0");
assertTrue(r.matches(Address("2001:db8:f00::ace:d00c")));
@@ -96,11 +98,11 @@
assertFalse(r.matches(Address("192.0.0.21")));
assertFalse(r.matches(Address("8.8.8.8")));
- RouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0");
+ PatchedRouteInfo ipv6Default = new PatchedRouteInfo(Prefix("::/0"), null, "rmnet0");
assertTrue(ipv6Default.matches(Address("2001:db8::f00")));
assertFalse(ipv6Default.matches(Address("192.0.2.1")));
- RouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0");
+ PatchedRouteInfo ipv4Default = new PatchedRouteInfo(Prefix("0.0.0.0/0"), null, "rmnet0");
assertTrue(ipv4Default.matches(Address("255.255.255.255")));
assertTrue(ipv4Default.matches(Address("192.0.2.1")));
assertFalse(ipv4Default.matches(Address("2001:db8::f00")));
diff --git a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
index 7d72f3e..d850c7c 100644
--- a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
+++ b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
@@ -21,7 +21,7 @@
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder;
+import android.view.inputmethod.CursorAnchorInfo.Builder;
public class CursorAnchorInfoTest extends InstrumentationTestCase {
// null represents a character that is invisible, for example because it's overlapped by some
@@ -64,7 +64,7 @@
Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
- final CursorAnchorInfoBuilder builder = new CursorAnchorInfoBuilder();
+ final Builder builder = new Builder();
builder.setSelectionRange(SELECTION_START, SELECTION_END)
.setComposingText(COMPOSING_TEXT_START, COMPOSING_TEXT)
.setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
@@ -148,7 +148,7 @@
final Matrix MATRIX3 = new Matrix();
MATRIX3.setTranslate(210.0f, 220.0f);
final Matrix matrix = new Matrix();
- final CursorAnchorInfoBuilder builder = new CursorAnchorInfoBuilder();
+ final Builder builder = new Builder();
matrix.set(MATRIX1);
builder.setMatrix(matrix);
@@ -171,7 +171,7 @@
public void testBuilderAdd() throws Exception {
// A negative index should be rejected.
try {
- new CursorAnchorInfoBuilder().addCharacterRect(-1, 0.0f, 0.0f, 0.0f, 0.0f);
+ new Builder().addCharacterRect(-1, 0.0f, 0.0f, 0.0f, 0.0f);
} catch (IllegalArgumentException ex) {
assertTrue(true);
}
@@ -191,4 +191,3 @@
}
}
}
-
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index c6bccfe..abb960c 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -106,6 +106,7 @@
ifeq ($(MINIMAL_FONT_FOOTPRINT),true)
$(eval $(call create-font-symlink,Roboto-Black.ttf,Roboto-Bold.ttf))
+$(eval $(call create-font-symlink,Roboto-BlackItalic.ttf,Roboto-BoldItalic.ttf))
$(eval $(call create-font-symlink,Roboto-Light.ttf,Roboto-Regular.ttf))
$(eval $(call create-font-symlink,Roboto-LightItalic.ttf,Roboto-Italic.ttf))
$(eval $(call create-font-symlink,Roboto-Medium.ttf,Roboto-Regular.ttf))
@@ -120,6 +121,7 @@
else # !MINIMAL_FONT
font_src_files += \
Roboto-Black.ttf \
+ Roboto-BlackItalic.ttf \
Roboto-Light.ttf \
Roboto-LightItalic.ttf \
Roboto-Medium.ttf \
diff --git a/data/fonts/Roboto-BlackItalic.ttf b/data/fonts/Roboto-BlackItalic.ttf
new file mode 100644
index 0000000..3ebdc7d
--- /dev/null
+++ b/data/fonts/Roboto-BlackItalic.ttf
Binary files differ
diff --git a/data/fonts/Roboto-MediumItalic.ttf b/data/fonts/Roboto-MediumItalic.ttf
index a30aa0c..b828205 100644
--- a/data/fonts/Roboto-MediumItalic.ttf
+++ b/data/fonts/Roboto-MediumItalic.ttf
Binary files differ
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index e5573bb..70fc6a2 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -25,6 +25,7 @@
Roboto-Italic.ttf \
Roboto-BoldItalic.ttf \
Roboto-Black.ttf \
+ Roboto-BlackItalic.ttf \
Roboto-Light.ttf \
Roboto-LightItalic.ttf \
Roboto-Medium.ttf \
diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml
index 646b33b..8c59fea 100644
--- a/data/fonts/system_fonts.xml
+++ b/data/fonts/system_fonts.xml
@@ -82,6 +82,7 @@
</nameset>
<fileset>
<file>Roboto-Black.ttf</file>
+ <file>Roboto-BlackItalic.ttf</file>
</fileset>
</family>
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index c61a94b..32b9c9e 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -64,7 +64,7 @@
</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on May 1, 2014.
+<p style="clear:both"><em>Data collected during a 7-day period ending on June 4, 2014.
<br/>Any versions with less than 0.1% distribution are not shown.</em>
</p>
@@ -95,7 +95,7 @@
</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on May 1, 2014.
+<p style="clear:both"><em>Data collected during a 7-day period ending on June 4, 2014.
<br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
@@ -114,7 +114,7 @@
<img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chs=400x250&cht=p&chd=t%3A0.1%2C87.0%2C12.9&chf=bg%2Cs%2C00000000&chl=GL%201.1%20only%7CGL%202.0%7CGL%203.0&chco=c4df9b%2C6fad0c" />
+src="//chart.googleapis.com/chart?chs=400x250&cht=p&chd=t%3A0.1%2C83.6%2C16.3&chf=bg%2Cs%2C00000000&chl=GL%201.1%20only%7CGL%202.0%7CGL%203.0&chco=c4df9b%2C6fad0c" />
<p>To declare which version of OpenGL ES your application requires, you should use the {@code
android:glEsVersion} attribute of the <a
@@ -136,17 +136,17 @@
</tr>
<tr>
<td>2.0</th>
-<td>87.0%</td>
+<td>83.6%</td>
</tr>
<tr>
<td>3.0</th>
-<td>12.9%</td>
+<td>16.3%</td>
</tr>
</table>
-<p style="clear:both"><em>Data collected during a 7-day period ending on May 1, 2014</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on June 4, 2014</em></p>
@@ -164,47 +164,42 @@
var VERSION_DATA =
[
{
- "chart": "//chart.googleapis.com/chart?chs=500x250&cht=p&chd=t%3A1.0%2C16.2%2C0.1%2C13.4%2C60.8%2C8.5&chf=bg%2Cs%2C00000000&chl=Froyo%7CGingerbread%7CHoneycomb%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat&chco=c4df9b%2C6fad0c",
+ "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat&chf=bg%2Cs%2C00000000&chd=t%3A0.8%2C14.9%2C12.3%2C58.4%2C13.6&chco=c4df9b%2C6fad0c&cht=p&chs=500x250",
"data": [
{
"api": 8,
"name": "Froyo",
- "perc": "1.0"
+ "perc": "0.8"
},
{
"api": 10,
"name": "Gingerbread",
- "perc": "16.2"
- },
- {
- "api": 13,
- "name": "Honeycomb",
- "perc": "0.1"
+ "perc": "14.9"
},
{
"api": 15,
"name": "Ice Cream Sandwich",
- "perc": "13.4"
+ "perc": "12.3"
},
{
"api": 16,
"name": "Jelly Bean",
- "perc": "33.5"
+ "perc": "29.0"
},
{
"api": 17,
"name": "Jelly Bean",
- "perc": "18.8"
+ "perc": "19.1"
},
{
"api": 18,
"name": "Jelly Bean",
- "perc": "8.5"
+ "perc": "10.3"
},
{
"api": 19,
"name": "KitKat",
- "perc": "8.5"
+ "perc": "13.6"
}
]
}
@@ -226,23 +221,22 @@
"xhdpi": "0.6"
},
"Normal": {
- "hdpi": "33.9",
- "mdpi": "12.5",
- "xhdpi": "19.9",
- "xxhdpi": "13.5"
+ "hdpi": "34.2",
+ "mdpi": "12.0",
+ "xhdpi": "19.6",
+ "xxhdpi": "14.6"
},
"Small": {
- "ldpi": "7.5"
+ "ldpi": "7.2"
},
"Xlarge": {
"hdpi": "0.3",
- "ldpi": "0.1",
- "mdpi": "4.2",
+ "mdpi": "4.0",
"xhdpi": "0.3"
}
},
- "densitychart": "//chart.googleapis.com/chart?chs=400x250&cht=p&chd=t%3A8.2%2C21.1%2C1.6%2C34.8%2C20.8%2C13.5&chf=bg%2Cs%2C00000000&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chco=c4df9b%2C6fad0c",
- "layoutchart": "//chart.googleapis.com/chart?chs=400x250&cht=p&chd=t%3A4.9%2C7.8%2C80.0%2C7.5&chf=bg%2Cs%2C00000000&chl=Xlarge%7CLarge%7CNormal%7CSmall&chco=c4df9b%2C6fad0c"
+ "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chf=bg%2Cs%2C00000000&chd=t%3A7.8%2C20.4%2C1.6%2C35.1%2C20.5%2C14.6&chco=c4df9b%2C6fad0c&cht=p&chs=400x250",
+ "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chf=bg%2Cs%2C00000000&chd=t%3A4.6%2C7.8%2C80.4%2C7.2&chco=c4df9b%2C6fad0c&cht=p&chs=400x250"
}
];
diff --git a/docs/html/distribute/engage/analytics.jd b/docs/html/distribute/engage/analytics.jd
new file mode 100644
index 0000000..5f7cade
--- /dev/null
+++ b/docs/html/distribute/engage/analytics.jd
@@ -0,0 +1,50 @@
+page.title=Understand User Behavior
+page.metaDescription=Use Google Analytics to learn what your users like and what keeps them coming back.
+page.tags="analytics, user behavior"
+page.image=/images/gp-analytics.jpg
+
+@jd:body
+
+<div class="figure">
+ <img src="{@docRoot}images/gp-analytics.jpg" style="width:320px">
+</div>
+
+
+<p>
+ Link your Google Play Developer Console with Google Analytics to learn much
+ more about how users interact with your app — before and after they
+ download it.
+</p>
+
+<p>
+ Start by discovering how many people visit your Google Play listing page,
+ where they come from, and how many go on to install your app. Once the app is
+ launched, use Google Analytics to see which of your features are most
+ popular, where power users spend their time, who tends to make in-app
+ purchases, and more.
+</p>
+
+<p>
+ Google Analytics delivers the numbers in real time, so you can act fast to
+ update your landing page and your app. <a href=
+ "http://www.google.com/analytics/mobile/">Learn more</a>.
+</p>
+
+<p>
+ If you have a Google Analytics account already, linking it to Google Play
+ takes just a few moments. You can also link your Google Analytics account to
+ Admob to start gaining more user insights to improve your in-app marketing.
+</p>
+
+ <div class="headerLine clearfloat">
+ <h2 id="related-resources">
+ Related Resources
+ </h2>
+</div>
+
+<div class="resource-widget resource-flow-layout col-13"
+ data-query="collection:distribute/engage/analytics"
+ data-sortorder="-timestamp"
+ data-cardsizes="9x3"
+ data-maxresults="6">
+</div>
\ No newline at end of file
diff --git a/docs/html/distribute/engage/deep-linking.jd b/docs/html/distribute/engage/deep-linking.jd
index 50728c5..a25c3c6 100644
--- a/docs/html/distribute/engage/deep-linking.jd
+++ b/docs/html/distribute/engage/deep-linking.jd
@@ -1,13 +1,13 @@
page.title=Deep Link to Bring Users Back
-page.metaDescription=Use deep links to bring your users into your apps from social posts or search.
+page.metaDescription=Use deep links to bring your users into your apps from social posts, search, or ads.
page.tags="app indexing, google+ signin"
page.image=/images/gp-listing-4.jpg
@jd:body
<p>
- Use deep links to bring your users into your apps from social posts or
- search.
+ Use deep links to bring your users into your apps from social posts,
+ search, or ads.
</p>
<div class="headerLine">
@@ -47,6 +47,11 @@
<h2>Deep Linking from Google Search — App Indexing</h2>
</div>
+
+<div style="float:right;">
+ <img src="/images/gp-listing-4.jpg" style="padding-top:1em;padding-left:2em;">
+</div>
+
<p>
Another way to bring users back to your apps is to apply for app indexing.
</p>
@@ -60,16 +65,32 @@
content</a>.
</p>
-<div>
- <img src="{@docRoot}images/gp-listing-4.jpg" style="padding-top:1em;">
+<div class="clearfloat" style="margin-top:2em;"></div>
+
+<div style="float:right;width:340px;padding-left:2em;">
+ <img src="/images/gp-ads-linking2.jpg" style="padding-top:1em;">
</div>
-<div class="headerLine">
+<div class="headerLine ">
+<h2>Deep Linking from Google Ads</h2>
+</div>
+<p>
+ Ads can remind users about the apps they already have.
+</p>
+
+<p>
+ As with deep links from Google's organic search results, AdWords deep links
+ send users directly to the relevant pages in apps they already have on their
+ mobile device. A mobile search for "flights to London," for instance, could
+ take a user straight to the London page in a travel app. <a href=
+ "http://www.thinkwithgoogle.com/products/ads-apps.html"
+ class="external-link">Learn more</a>.
+</p>
+
+<div class="headerLine clearfloat">
<h2 id="related-resources">
Related Resources
</h2>
-
-
</div>
<div class="resource-widget resource-flow-layout col-13" data-query=
diff --git a/docs/html/distribute/engage/engage_toc.cs b/docs/html/distribute/engage/engage_toc.cs
index 0314f8c..596051a 100644
--- a/docs/html/distribute/engage/engage_toc.cs
+++ b/docs/html/distribute/engage/engage_toc.cs
@@ -37,6 +37,12 @@
</li>
<li class="nav-section">
<div class="nav-section empty" style="font-weight:normal"><a href="<?cs
+ var:toroot?>distribute/engage/analytics.html">
+ <span class="en">Understand User Behavior</span></a>
+ </div>
+ </li>
+ <li class="nav-section">
+ <div class="nav-section empty" style="font-weight:normal"><a href="<?cs
var:toroot?>distribute/engage/app-updates.html">
<span class="en">Update Regularly</span></a>
</div>
diff --git a/docs/html/distribute/engage/index.jd b/docs/html/distribute/engage/index.jd
index f8cd1ee..2b103c3 100644
--- a/docs/html/distribute/engage/index.jd
+++ b/docs/html/distribute/engage/index.jd
@@ -15,8 +15,8 @@
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/engagelanding"
- data-cardSizes="6x6"
- data-maxResults="9">
+ data-cardSizes="9x6,9x6,6x6,6x6,6x6,9x6,9x6,6x6,6x6,6x6"
+ data-maxResults="10">
</div>
<h3>Related Resources</h3>
diff --git a/docs/html/distribute/engage/widgets.jd b/docs/html/distribute/engage/widgets.jd
index 286adea..6adb55c 100644
--- a/docs/html/distribute/engage/widgets.jd
+++ b/docs/html/distribute/engage/widgets.jd
@@ -1,5 +1,5 @@
page.title=Build Useful Widgets
-page.metaDescription=Use widgets to remind users about important information in your apps and games, even when your apps are closed.
+page.metaDescription=Use home screen widgets to remind users about important information in your apps and games, even when your apps are closed.
page.tags=""
page.image=/images/gp-engage-0.jpg
diff --git a/docs/html/distribute/essentials/quality/core.jd b/docs/html/distribute/essentials/quality/core.jd
index c301f8c..cfe1a2a 100644
--- a/docs/html/distribute/essentials/quality/core.jd
+++ b/docs/html/distribute/essentials/quality/core.jd
@@ -234,9 +234,7 @@
</tr>
</table>
-<h3>
- Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/essentials/corequalityguidelines/visualdesign"
@@ -509,9 +507,7 @@
</tr>
</table>
-<h3>
- Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/essentials/corequalityguidelines/functionality"
@@ -670,9 +666,7 @@
</tr>
</table>
-<h3>
- Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/essentials/core/performance" data-sortorder="-timestamp"
@@ -819,9 +813,7 @@
</tr>
</table>
-<h3>
- Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/essentials/core/play" data-sortorder="-timestamp"
diff --git a/docs/html/distribute/essentials/quality/tablets.jd b/docs/html/distribute/essentials/quality/tablets.jd
index 966c462..2b2a5ae 100644
--- a/docs/html/distribute/essentials/quality/tablets.jd
+++ b/docs/html/distribute/essentials/quality/tablets.jd
@@ -158,7 +158,7 @@
multi-pane UI for tablets (see next section).</li>
</ul>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/optimize"
@@ -219,7 +219,7 @@
<code>sw600dp</code>/<code>sw720</code>).</li>
</ul>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/extrascreen"
@@ -308,7 +308,7 @@
it will request the {@code xxhdpi} version of the launcher icon.</li>
</ul>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/assets"
@@ -345,7 +345,7 @@
or just centering the icon within the transparent button.</li>
</ul>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/fonts"
@@ -371,7 +371,7 @@
possible.</li>
</ul>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/widgets"
@@ -458,7 +458,7 @@
</li>
</ol>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/versions"
@@ -528,7 +528,7 @@
as needed.
</p>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/hardware"
@@ -560,7 +560,7 @@
<a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html"><code><compatible-screens></code></a>
element in your app.</p>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/tabletscreens"
@@ -689,7 +689,7 @@
</li>
</ul>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/showcase"
@@ -799,7 +799,7 @@
recommended.
</p>
-<h3 class="clearfloat">Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/essentials/tabletguidelines/googleplay"
data-sortOrder="-timestamp"
diff --git a/docs/html/distribute/googleplay/edu/about.jd b/docs/html/distribute/googleplay/edu/about.jd
index 1c003cf..e73356e 100644
--- a/docs/html/distribute/googleplay/edu/about.jd
+++ b/docs/html/distribute/googleplay/edu/about.jd
@@ -109,7 +109,7 @@
<p style="clear:both">
</p>
<div class="headerLine">
-<h2>Related Resources</h2>
+<h2 id="related-resources">Related Resources</h2>
</div>
<div class="dynamic-grid">
diff --git a/docs/html/distribute/monetize/ads.jd b/docs/html/distribute/monetize/ads.jd
index bcb1e52..9a847ff 100644
--- a/docs/html/distribute/monetize/ads.jd
+++ b/docs/html/distribute/monetize/ads.jd
@@ -10,17 +10,32 @@
</div>
<p>
- In-app advertising offers a quick and easy way to incorporate a monetization
- option into both your <a href=
+ Ads can be a quick and easy way to earn more from your <a href=
"{@docRoot}distribute/monetize/freemium.html">freemium</a>, <a href=
"{@docRoot}distribute/monetize/premium.html">premium</a>, and <a href=
- "{@docRoot}distribute/monetize/subscriptions.html">subscription</a> apps. </p>
+ "{@docRoot}distribute/monetize/subscriptions.html">subscription</a> apps.
+ AdMob and the Google Mobile Ads SDK let you add advertising to your apps with
+ just a few lines of code.
+</p>
-<p>Using <a href=
+<p>
+ The question is: which model gets the best results for your app? Google's ad
+ tools are made to help you figure out what combination works best for both
+ your audience and your bottom line. </p>
+
+<p>Start by linking your AdMob and Google
+ Analytics accounts to get better insights and more earning power: for
+ instance, AdMob can promote in-app purchases to the people who buy them most
+ often, while showing income-generating ads to those less likely to buy right
+ now.
+</p>
+
+<p>
+ Using <a href=
"http://www.google.com/ads/admob/monetize.html#subid=us-en-et-dac">AdMob</a>
- and the <a href="{@docRoot}google/play-services/ads.html">Google
- Mobile Ads SDK</a> included in Google Play Services, you’re able to add
- advertising into your apps, with just a few lines of code.
+ and the <a href="{@docRoot}google/play-services/ads.html">Google Mobile Ads
+ SDK</a> included in Google Play Services, you’re able to add advertising into
+ your apps, with just a few lines of code.
</p>
<p>
@@ -30,36 +45,33 @@
<ul>
<li>
<p>
- <strong>Placement within your apps</strong> — Well placed ads will
- optimize your revenue by making it more likely that users will ‘click
- through’. Poorly placed ads can result in low click-through rates, and in
- the worse cases poor rating and users rapidly abandoning your apps. You
- can get advice on how to best place ads from the developer training on
- <a href=
- "{@docRoot}training/monetization/ads-and-ux.html">using
- ads</a>.
+ <strong>Placement within your apps</strong> — Well-placed ads make
+ it more likely that users will click through and convert. Poorly-placed
+ ads lead to lower click-through rates, and even poor ratings and users
+ abandoning your apps. Our <a href=
+ "{@docRoot}training/monetization/ads-and-ux.html">developer training</a>
+ on using ads shows some of the best ways to place ads.
</p>
</li>
<li>
<p>
<strong>Ad formats</strong> — Every app offers a different type of
- experience for users, so it’s important to consider the format of ads
- you’re using to ensure it’s compatible with the experience. While banner
- ads may work well for a flashlight utility app, an immersive gaming app
- may benefit more from a video interstitial. Mismatched ad formats may
- negatively affect your users’ experience and ad revenue, so try to select
- formats that fit well with the content and flow of your apps.
+ experience for users, so it’s important that your ad formats match that
+ experience. While banner ads may work well for a flashlight utility app,
+ an immersive gaming app may benefit more from a video interstitial.
+ Mismatched ad formats can make users unhappy and leave money on the
+ table.
</p>
</li>
<li>
<p>
- <strong>Maximizing your performance</strong> — Ensure you’re optimizing
- your advertising revenue by maximizing your CPMs <em>and</em> fill rate.
- Often ad providers will cite very high CPMs but will have a low fill rate
- that can severely decrease your effective CPM, so look at both of these
- figures. Also consider using a <a href=
+ <strong>Maximizing your performance</strong> — Make sure you’re
+ optimizing your advertising revenue by maximizing your CPMs and fill
+ rate. Ad providers often cite their very high CPMs but don't mention low
+ fill rates that can severely decrease your effective CPM. Be sure to look
+ at both of these figures. Consider using a <a href=
"https://support.google.com/admob/v2/answer/3063564?hl=en&ref_topic=3063091#subid=us-en-et-dac">
mediation</a> solution if you’d like to use multiple ad providers in your
apps. Look for solutions that offer yield management or <a href=
@@ -71,30 +83,43 @@
<li>
<p>
- <strong>Exercising control options</strong> — A variety of ads promoting a
- broad selection of other services or apps may show up within you apps.
- Depending on your goals and the type of experience you want to provide
- your users, it may make sense to <a href=
+ <strong>Exercising control options</strong> — A variety of ads may
+ show up within your app. It may make sense to <a href=
"https://support.google.com/admob/v2/answer/3150235?hl=enl#subid=us-en-et-dac">
- block</a> certain advertisements from appearing. Some developers don’t
- want apps in a similar category showing to their users, but some don’t
- mind.
+ block</a> certain of those advertisements from appearing, depending on
+ your goals and the type of experience you want to provide. Some
+ developers, for instance, don’t want ads for apps in their same category
+ showing to their users, while others don’t mind at all.
</p>
</li>
<li>
<p>
- <strong>Cross promoting your other apps</strong> — Ads can be used for
- more than just earning revenue. Consider using <a href=
+ <strong>Cross promoting your other apps</strong> — Ads can do more
+ than earn revenue. Consider running <a href=
"https://support.google.com/admob/v2/answer/3210452?hl=en#subid=us-en-et-dac">
- house ads</a> within your apps to create awareness and promote your
- entire portfolio of apps. When launching new apps, an easy way to quickly
- attract users is to promote directly to your existing customers.
+ house ads</a> within your apps to promote other apps in your portfolio.
+ When you launch a new app, this kind of promotion is a free and easy way
+ to attract new users quickly.
</p>
</li>
</ul>
<p>
+ Don't forget that paid channels like AdWords and YouTube can help you cast a
+ wider net by reaching targeted audiences outside the app ecosystem. They're a
+ great way to find new users at a price that you control. <a href=
+ "https://support.google.com/adwords/answer/2549053">Learn more</a>.
+</p>
+
+<p>
+ To start monetizing with ads, sign up for AdMob and integrate the Google
+ Mobile Ads SDK into your apps. If you also need to manage direct deals with
+ advertisers, consider using DoubleClick for Publishers Small Business.
+</p>
+
+
+<p>
To start monetizing with ads sign up for <a href=
"http://www.google.com/ads/admob/#subid=us-en-et-dac">AdMob</a> and integrate
the <a href="https://developers.google.com/mobile-ads-sdk/download">Google
diff --git a/docs/html/distribute/tools/launch-checklist.jd b/docs/html/distribute/tools/launch-checklist.jd
index 3f6b1a6..f310800 100644
--- a/docs/html/distribute/tools/launch-checklist.jd
+++ b/docs/html/distribute/tools/launch-checklist.jd
@@ -85,9 +85,7 @@
Play.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/understanding"
@@ -110,9 +108,7 @@
repeated violations, termination of your developer account.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/policies" data-sortorder=
@@ -154,9 +150,7 @@
should exhibit.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/quality" data-sortorder=
@@ -212,9 +206,7 @@
no changes are required in your app binary.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/rating" data-sortorder=
@@ -280,9 +272,7 @@
Checklist</a> for key steps and considerations in the localization process.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/country" data-sortorder=
@@ -335,9 +325,7 @@
on your code when building your release-ready APK.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/size" data-sortorder=
@@ -387,9 +375,7 @@
<a href="{@docRoot}about/dashboards/index.html">Device Dashboard</a> charts.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/platform" data-sortorder=
@@ -450,9 +436,7 @@
set up a Google Wallet Merchant Account</a> before you can publish.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/price" data-sortorder=
@@ -485,9 +469,7 @@
complete and test your implementation before creating your release-ready APK.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/purchasemethod"
@@ -517,9 +499,7 @@
available currencies through the Developer Console.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/setprice" data-sortorder=
@@ -599,9 +579,7 @@
listing.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/localization"
@@ -659,9 +637,7 @@
publishing date.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/graphics" data-sortorder=
@@ -701,7 +677,7 @@
Developer Console. If necessary, you can replace an APK with a more recent
version before publishing.
</p>
-<!--<h3>Related resources</h3>
+<!--<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/toolsreference/launchchecklist/build"
@@ -815,9 +791,7 @@
elsewhere.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/productdetails"
@@ -853,9 +827,7 @@
available.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/badges" data-sortorder=
@@ -959,9 +931,7 @@
linking from your promotional campaigns.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/launchchecklist/finalchecks"
@@ -1063,7 +1033,7 @@
</ul>
</ul>
-<h3>Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/toolsreference/launchchecklist/afterlaunch"
diff --git a/docs/html/distribute/tools/localization-checklist.jd b/docs/html/distribute/tools/localization-checklist.jd
index 569ed02..08a8143 100644
--- a/docs/html/distribute/tools/localization-checklist.jd
+++ b/docs/html/distribute/tools/localization-checklist.jd
@@ -84,9 +84,7 @@
development, translation, testing, and marketing efforts to these markets.
</p>
-<h3 id="related-resources">
- Related Resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/localizationchecklist/identifylocales"
@@ -224,9 +222,7 @@
directories, without language or locale qualifiers.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/tools/loc/designforloc" data-sortorder="-timestamp"
@@ -399,9 +395,7 @@
</resources>
</pre>
-<h3 class="clearfloat">
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/localizationchecklist/managestrings"
@@ -570,9 +564,7 @@
<img src="{@docRoot}images/gp-localization-trans-0.png" class="border-img">
</div>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/localizationchecklist/translatestrings"
@@ -690,7 +682,7 @@
your localized apps. One way to do that is through beta testing with regional
users — Google Play can help you do this. <!-- </p>
-<h3 class="clearfloat">Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/toolsreference/localizationchecklist/test"
@@ -887,9 +879,7 @@
helpful reminders for a successful localized launch.
</p>
-<h3>
- Related resources
-</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13" data-query=
"collection:distribute/toolsreference/localizationchecklist/preplaunch"
@@ -968,7 +958,7 @@
"{@docRoot}distribute/tools/launch-checklist.html">Launch Checklist</a> to
learn more about how to plan, build, and launch your app on Google Play.
</p>
-<h3 class="clearfloat">Related resources</h3>
+<h3 class="rel-resources clearfloat">Related resources</h3>
<div class="resource-widget resource-flow-layout col-13"
data-query="collection:distribute/toolsreference/localizationchecklist/supportlaunch"
diff --git a/docs/html/distribute/tools/promote/badge-files.jd b/docs/html/distribute/tools/promote/badge-files.jd
index b481802..e65e698 100644
--- a/docs/html/distribute/tools/promote/badge-files.jd
+++ b/docs/html/distribute/tools/promote/badge-files.jd
@@ -21,9 +21,10 @@
<a href="{@docRoot}downloads/brand/v2/english_get.ai">English (English)</a><br/>
+ <a href="{@docRoot}downloads/brand/af_generic_rgb_wo.ai">Afrikaans (Afrikaans)</a><br/>
+
<a href="{@docRoot}downloads/brand/v2/amharic_get.ai">ኣማርኛ (Amharic)</a><br/>
- <a href="{@docRoot}downloads/brand/af_generic_rgb_wo.ai">Afrikaans (Afrikaans)</a><br/>
<!--
<a href="{@docRoot}downloads/brand/ar_generic_rgb_wo.ai">العربية (Arabic)</a><br/>
-->
@@ -286,6 +287,3 @@
<p>To quickly create a badge that links to your apps on Google Play,
use the <a
href="{@docRoot}distribute/tools/promote/badges.html">Googe Play badge generator</a>.</p>
-
-
-
\ No newline at end of file
diff --git a/docs/html/distribute/users/index.jd b/docs/html/distribute/users/index.jd
index 77ef609..a810f36 100644
--- a/docs/html/distribute/users/index.jd
+++ b/docs/html/distribute/users/index.jd
@@ -14,8 +14,8 @@
<div class="resource-widget resource-flow-layout landing col-16"
data-query="collection:distribute/users"
- data-cardSizes="6x6, 6x6, 6x6, 9x6, 9x6, 6x6, 6x6, 6x6"
- data-maxResults="8">
+ data-cardSizes="6x6"
+ data-maxResults="6">
</div>
<h3>Related resources</h3>
diff --git a/docs/html/distribute/users/promote-with-ads.jd b/docs/html/distribute/users/promote-with-ads.jd
new file mode 100644
index 0000000..1e28ae1
--- /dev/null
+++ b/docs/html/distribute/users/promote-with-ads.jd
@@ -0,0 +1,45 @@
+page.title=Promote Your App with Ads
+page.metaDescription=Promote your app through AdMob, AdWords, and YouTube to find new users at the right moment.
+page.image=/images/gp-ads-console.jpg
+page.tags="users, ads, analytics"
+
+@jd:body
+
+<p>
+ AdMob is Google's advertising platform for mobile apps. You can use it to
+ monetize your app and promote your apps, and you can link your Google
+ Analytics account to AdMob so you can analyze your apps — all in one
+ place.
+</p>
+
+<p>
+ <a href="http://www.google.com/ads/admob/">AdMob</a> is the largest mobile ad
+ app network. But you get more than just massive scale: AdMob will soon help
+ you find the right users in related apps. If your app is for bicycling, AdMob
+ can promote your app on other fitness and cycling-related apps worldwide.
+ <a href=
+ "https://apps.admob.com/admob/signup?subid=us-en-et-dac&_adc=ww-ww-et-admob2&hl=en">
+ Sign up for AdMob</a>.
+</p>
+
+<p>
+ AdMob also offers new solutions to help you achieve app-related goals such as
+ downloads, re-engagement and in-app purchases using Google search and the
+ Google Display Network. These solutions include streamlined campaign creation
+ flows and tools to track performance across the entire app lifecycle.
+ <a href="https://support.google.com/adwords/answer/2549053?hl=en">Learn
+ More</a>.
+</p>
+<div style="margin-top:2em;">
+ <img src="{@docRoot}images/gp-ads-console.jpg">
+</div>
+
+<div class="headerLine">
+<h2 id="related-resources">Related Resources</h2>
+</div>
+
+<div class="resource-widget resource-flow-layout col-13"
+ data-query="collection:distribute/users/promotewithads"
+ data-sortOrder="-timestamp"
+ data-cardSizes="9x3"
+ data-maxResults="6"></div>
\ No newline at end of file
diff --git a/docs/html/distribute/users/users_toc.cs b/docs/html/distribute/users/users_toc.cs
index a2437a6..1f173cb 100644
--- a/docs/html/distribute/users/users_toc.cs
+++ b/docs/html/distribute/users/users_toc.cs
@@ -28,7 +28,12 @@
</a>
</div>
</li>
-
+ <li class="nav-section">
+ <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/users/promote-with-ads.html">
+ <span class="en">Promote with Ads</span>
+ </a>
+ </div>
+ </li>
</ul>
diff --git a/docs/html/google/play-services/setup.jd b/docs/html/google/play-services/setup.jd
index d502e8d..744e191 100644
--- a/docs/html/google/play-services/setup.jd
+++ b/docs/html/google/play-services/setup.jd
@@ -95,7 +95,9 @@
<li>Open the <code>build.gradle</code> file inside your application module directory.
<p class="note"><strong>Note:</strong> Android Studio projects contain a top-level
<code>build.gradle</code> file and a <code>build.gradle</code> file for each module.
- Be sure to edit the file for your application module.</p></li>
+ Be sure to edit the file for your application module. See
+ <a href="{@docRoot}sdk/installing/studio-build.html">Building Your Project with
+ Gradle</a> for more information about Gradle.</p></li>
<li>Add a new build rule under <code>dependencies</code> for the latest version of
<code>play-services</code>. For example:
<pre class="no-pretty-print">
diff --git a/docs/html/guide/components/fundamentals.jd b/docs/html/guide/components/fundamentals.jd
index 9ac063e..fd1a7a8 100644
--- a/docs/html/guide/components/fundamentals.jd
+++ b/docs/html/guide/components/fundamentals.jd
@@ -335,8 +335,8 @@
{@link android.content.Intent} to start activities, services, and broadcast receivers. You can do so
by explicitly naming the target component (using the component class name) in the intent. However,
the real power of intents lies in the concept of <em>implicit intents</em>. An implicit intent
-simply describe the type of action to perform (and optionally, the data upon which you’d like to
-perform the action) and allow the system to find a component on the device that can perform the
+simply describes the type of action to perform (and, optionally, the data upon which you’d like to
+perform the action) and allows the system to find a component on the device that can perform the
action and start it. If there are multiple components that can perform the action described by the
intent, then the user selects which one to use.</p>
diff --git a/docs/html/guide/practices/verifying-apps-art.jd b/docs/html/guide/practices/verifying-apps-art.jd
new file mode 100644
index 0000000..0eedfaf
--- /dev/null
+++ b/docs/html/guide/practices/verifying-apps-art.jd
@@ -0,0 +1,296 @@
+page.title=Verifying App Behavior on the Android Runtime (ART)
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+<h2>Quickview</h2>
+ <ul>
+ <li>The new Android runtime (ART) is available on some of the newest Android
+ devices, though all of them currently have Dalvik as the default
+ runtime.</li>
+ <li>App developers should make sure their apps are compatible with ART,
+ especially if you use JNI to run native code or if you use certain tools
+ that produce non-standard code (such as some obfuscators).</li>
+ </ul>
+
+ <h2 id="Contents">In this document</h2>
+ <ol>
+ <li><a href="#GC_Migration">Addressing Garbage Collection (GC) Issues</a></li>
+ <li><a href="#JNI_Issues">Preventing JNI Issues</a>
+ <ol>
+ <li><a href="#JNI_and_GC">Checking JNI code for garbage-collection
+ issues</a></li>
+ <li><a href="#Error_Handling">Error handling</a></li>
+ <li><a href="#Object_Model_Changes">Object model changes</a></li>
+ </ol>
+ </li>
+ <li><a href="#Stack_Size">Preventing Stack Size Issues</a></li>
+ <li><a href="#AOT_Fails">Fixing AOT Compilation Issues</a></li>
+ <li><a href="#Reporting_Problems">Reporting Problems</a></li>
+ </ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="http://source.android.com/devices/tech/dalvik/art.html">Introducing ART</a></li>
+ <li><a
+href="http://android-developers.blogspot.com/2011/07/debugging-android-jni-with-checkjni.html">Debugging
+Android JNI with CheckJNI</a></li>
+ </ol>
+</div>
+</div>
+
+<p>With Android 4.4, we are beginning to roll out a new Android runtime,
+<strong>ART</strong>. This runtime offers a number of new features that improve
+performance and smoothness of the Android platform and apps. (You can find more
+information about ART's new features in <a
+href="http://source.android.com/devices/tech/dalvik/art.html">Introducing
+ART</a>.)</p>
+
+<p>Currently, ART is available on a number of Android 4.4 devices, such as the
+Nexus 4, Nexus 5, Nexus 7, and Google Play edition devices.
+At this time, all devices still use Dalvik as the default runtime. We encourage
+you to test your apps for ART compatibility and to take advantage of ART's new
+features. However, for the time being, you should also take care to maintain
+compatibility with Dalvik.</p>
+
+<p>This document lets you know about things to watch for when migrating an
+existing app to be compatible with ART. Most apps should just work when
+running with ART. However, some techniques that work on Dalvik do not work on
+ART. This document discusses some of these issues.</p>
+
+<h2 id="GC_Migration">Addressing Garbage Collection (GC) Issues</h2>
+
+<p>Under Dalvik, apps frequently find it useful to explicitly call {@link
+java.lang.System#gc() System.gc()} to prompt garbage collection (GC). This should be
+far less necessary with ART, particularly if you're invoking garbage collection
+to prevent <a
+href="{@docRoot}/tools/debugging/debugging-memory.html#LogMessages"><code>GC_FOR_ALLOC</code></a>-type
+occurrences or to reduce fragmentation. You can verify which runtime is in use
+by calling {@link java.lang.System#getProperty(java.lang.String)
+System.getProperty("dalvik.vm.version")}. If ART is in use, the property's value
+is <code>"2.0.0"</code> or higher.</p>
+
+<p>Furthermore, a compacting garbage collector is under development in the <a
+href="https://source.android.com">Android Open-Source Project (AOSP)</a> to
+improve memory management. Because of this, you should avoid using techniques
+that are incompatible with compacting GC (such as saving pointers to object
+instance data). This is particularly important for apps that make use of the
+Java Native Interface (JNI). For more information, see <a
+href="#JNI_Issues">Preventing JNI Issues</a>.</p>
+
+<h2 id="JNI_Issues">Preventing JNI Issues</h2>
+
+<p>ART's JNI is somewhat stricter than Dalvik's. It is an especially good idea
+to use CheckJNI mode to catch common problems. If your app makes use of C/C++
+code, you should review the following article:</p>
+
+<p><a
+href="http://android-developers.blogspot.com/2011/07/debugging-android-jni-with-checkjni.html">Debugging
+Android JNI with CheckJNI</a></p>
+
+<h3 id="JNI_and_GC">Checking JNI code for garbage-collection issues</h3>
+
+<p>ART has a compacting garbage collector under development on the
+Android Open Source Project (AOSP). Once the compacting garbage collector is in
+use, objects may be moved in memory. If you use C/C++ code, do not
+perform operations that are incompatible with compacting GC. We have enhanced
+CheckJNI to identify some potential issues (as described in <a
+href="http://android-developers.blogspot.com/2011/11/jni-local-reference-changes-in-ics.html">JNI
+Local Reference Changes in ICS</a>).</p>
+
+<p>One area to watch for in particular is the use of
+<code>Get...ArrayElements()</code> and <code>Release...ArrayElements()</code>
+functions. In runtimes with non-compacting GC, the
+<code>Get...ArrayElements()</code> functions typically return a reference to the
+actual memory backing the array object. If you make a change to one of the
+returned array elements, the array object is itself changed (and the arguments
+to <code>Release...ArrayElements()</code> are usually ignored). However, if
+compacting GC is in use, the <code>Get...ArrayElements()</code> functions may
+return a copy of the memory. If you misuse the reference when compacting GC is
+in use, this can lead to memory corruption or other problems. For example:</p>
+
+<ul>
+
+ <li>If you make any changes to the returned array elements, you must call the
+ appropriate <code>Release...ArrayElements()</code> function when you are done,
+ to make sure the changes you made are correctly copied back to the underlying
+ array object.</li>
+
+ <li>When you release the memory array elements, you must use the appropriate
+ mode, depending on what changes you made:
+
+ <ul>
+
+ <li>If you did not make any changes to the array elements, use
+ <code>JNI_ABORT</code> mode, which releases the memory without copying
+ changes back to the underlying array object.</li>
+
+ <li>If you made changes to the array, and do not need the reference any
+ more, use code <code>0</code> (which updates the array object and frees
+ the copy of the memory).</li>
+
+ <li>If you made changes to the array that you want to commit, and you want
+ to keep the copy of the array, use <code>JNI_COMMIT</code> (which updates
+ the underlying array object and retains the copy).</li>
+
+ </ul>
+
+ </li>
+
+ <li>When you call <code>Release...ArrayElements()</code>, return the same
+ pointer that was originally returned by <code>Get...ArrayElements()</code>. For
+ example, it's not safe to increment the original pointer (to scan through the
+ returned array elements) then pass the incremented pointer to
+ <code>Release...ArrayElements()</code>. Passing this modified pointer can cause
+ the wrong memory to be freed, resulting in memory corruption.</li>
+
+</ul>
+
+<h3 id="Error_Handling">Error handling</h3>
+
+<p>ART's JNI throws errors in a number of cases where Dalvik didn’t. (Once
+again, you can catch many such cases by testing with CheckJNI.)</p>
+
+<p>For example, if <code>RegisterNatives</code> is called with a method that
+does not exist (perhaps because the method was removed by a tool such as
+<strong>ProGuard</strong>), ART now properly throws {@link
+java.lang.NoSuchMethodError}:</p>
+
+<pre class="no-pretty-print">
+08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main
+08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError:
+ no static or non-static method
+ "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
+08-12 17:09:41.082 13823 13823 E AndroidRuntime:
+ at java.lang.Runtime.nativeLoad(Native Method)
+08-12 17:09:41.082 13823 13823 E AndroidRuntime:
+ at java.lang.Runtime.doLoad(Runtime.java:421)
+08-12 17:09:41.082 13823 13823 E AndroidRuntime:
+ at java.lang.Runtime.loadLibrary(Runtime.java:362)
+08-12 17:09:41.082 13823 13823 E AndroidRuntime:
+ at java.lang.System.loadLibrary(System.java:526)
+</pre>
+
+<p>ART also logs an error (visible in logcat) if <code>RegisterNatives</code> is
+called with no methods:</p>
+
+<pre class="no-pretty-print">
+W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
+methods for <classname>
+</pre>
+
+<p>In addition, the JNI functions <code>GetFieldID()</code> and
+<code>GetStaticFieldID()</code> now properly throw {@link java.lang.NoSuchFieldError}
+instead of simply returning null. Similarly, <code>GetMethodID()</code> and
+<code>GetStaticMethodID()</code> now properly throw {@link java.lang.NoSuchMethodError}.
+This can lead to CheckJNI failures because of the unhandled exceptions or the
+exceptions being thrown to Java callers of native code. This makes it
+particularly important to test ART-compatible apps with CheckJNI mode.</p>
+
+<p>ART expects users of the JNI <code>CallNonvirtual...Method()</code> methods
+(such as <code>CallNonvirtualVoidMethod()</code>) to use the method's declaring
+class, not a subclass, as required by the JNI specification.</p>
+
+<h2 id="Stack_Size">Preventing Stack Size Issues</h2>
+
+<p>Dalvik had separate stacks for native and Java code, with a default Java
+stack size of 32KB and a default native stack size of 1MB. ART has a unified
+stack for better locality. Ordinarily, the ART {@link java.lang.Thread} stack
+size should be approximately the same as for Dalvik. However, if you explicitly
+set stack sizes, you may need to revisit those values for apps running in
+ART.</p>
+
+<ul>
+
+ <li>In Java, review calls to the {@link
+ java.lang.Thread#Thread(java.lang.ThreadGroup, java.lang.Runnable,
+ java.lang.String, long) Thread} constructor that specify an explicit stack
+ size. For example, you will need to increase the size if {@link
+ java.lang.StackOverflowError} occurs.</li>
+
+ <li>In C/C++, review use of <code>pthread_attr_setstack()</code> and
+ <code>pthread_attr_setstacksize()</code> for threads that also run Java code via
+ JNI. Here is an example of the error logged when an app attempts to call JNI
+ <code>AttachCurrentThread()</code> when the pthread size is too small:
+
+<pre class="no-pretty-print">F/art: art/runtime/thread.cc:435]
+ Attempt to attach a thread with a too-small stack (16384 bytes)</pre>
+ </li>
+
+</ul>
+
+<h2 id="Object_Model_Changes">Object model changes</h2>
+
+<p>Dalvik incorrectly allowed subclasses to override package-private methods.
+ART issues a warning in such cases:</p>
+
+<pre class="no-pretty-print">
+Before Android 4.1, method void com.foo.Bar.quux()
+would have incorrectly overridden the package-private method in
+com.quux.Quux
+</pre>
+
+<p>If you intend to override a class's method in a different package, declare the
+method as <code>public</code> or <code>protected</code>.</p>
+
+<p>{@link java.lang.Object} now has private fields. Apps that reflect on fields
+in their class hierarchies should be careful not to attempt to look at the
+fields of {@link java.lang.Object}. For example, if you are iterating up a class
+hierarchy as part of a serialization framework, stop when
+
+<pre>Class.getSuperclass() == java.lang.Object.class</pre>
+
+instead of continuing until the method returns <code>null</code>.</p>
+
+<p>Proxy {@link
+java.lang.reflect.InvocationHandler#invoke(java.lang.Object,java.lang.reflect.Method,java.lang.Object[])
+InvocationHandler.invoke()} now receives <code>null</code> if there are no
+arguments instead of an empty array. This behavior was documented previously but
+not correctly handled in Dalvik. Previous versions of <a
+href="https://code.google.com/p/mockito/">Mockito</a> have difficulties with
+this, so use an updated Mockito version when testing with ART.</p>
+
+<h2 id="AOT_Fails">Fixing AOT Compilation Issues</h2>
+
+<p>ART's Ahead-Of-Time (AOT) Java compilation should work for all standard Java
+code. Compilation is performed by ART's
+<code>dex2oat</code> tool; if you encounter any issues related to
+<code>dex2oat</code> at install time, let us know (see <a
+href="#Reporting_Problems">Reporting Problems</a>) so we can fix them as quickly
+as possible. A couple of issues to note:</p>
+
+<ul>
+
+ <li>ART does tighter bytecode verification at install time than Dalvik does.
+ Code produced by the Android build tools should be fine. However, some
+ post-processing tools (especially tools that perform obfuscation) may produce
+ invalid files that are tolerated by Dalvik but rejected by ART. We have been
+ working with tool vendors to find and fix such issues. In many cases, getting
+ the latest versions of your tools and regenerating the DEX files can fix these
+ problems.</li>
+
+ <li>Some typical problems that are flagged by the ART verifier include:
+ <ul>
+ <li>invalid control flow</li>
+ <li>unbalanced <code>moniterenter</code>/<code>moniterexit</code></li>
+ <li>0-length parameter type list size</li>
+ </ul>
+ </li>
+
+ <li>Some apps have dependencies on the installed <code>.odex</code> file
+ format in <code>/system/framework</code>, <code>/data/dalvik-cache</code>, or
+ in {@link dalvik.system.DexClassLoader}’s optimized output directory. These
+ files are now ELF files and not an extended form of DEX files. While ART tries
+ to follow the same naming and locking rules as Dalvik, apps should not depend
+ on the file format; the format is subject to change without notice.</li>
+
+
+
+<h2 id="Reporting_Problems">Reporting Problems</h2>
+
+<p>If you run into any issues that aren’t due to app JNI issues, report
+them via the Android Open Source Project Issue Tracker at <a
+href="https://code.google.com/p/android/issues/list">https://code.google.com/p/android/issues/list</a>.
+Include an <code>"adb bugreport"</code> and a link to the app in the Google
+Play store if available. Otherwise, if possible, attach an APK that reproduces
+the issue. Note that issues (including attachments) are publicly
+visible.</p>
diff --git a/docs/html/images/gp-ads-console.jpg b/docs/html/images/gp-ads-console.jpg
new file mode 100644
index 0000000..158e31d
--- /dev/null
+++ b/docs/html/images/gp-ads-console.jpg
Binary files differ
diff --git a/docs/html/images/gp-ads-linking2.jpg b/docs/html/images/gp-ads-linking2.jpg
new file mode 100644
index 0000000..0c2f731
--- /dev/null
+++ b/docs/html/images/gp-ads-linking2.jpg
Binary files differ
diff --git a/docs/html/images/gp-analytics.jpg b/docs/html/images/gp-analytics.jpg
new file mode 100644
index 0000000..e1a92c7
--- /dev/null
+++ b/docs/html/images/gp-analytics.jpg
Binary files differ
diff --git a/docs/html/images/training/volley-request.png b/docs/html/images/training/volley-request.png
new file mode 100644
index 0000000..85f0681
--- /dev/null
+++ b/docs/html/images/training/volley-request.png
Binary files differ
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index 8a4ac47..5ee01f3 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -49,7 +49,8 @@
"distribute/users/your-listing.html",
"distribute/users/build-buzz.html",
"distribute/users/build-community.html",
- "distribute/users/expand-to-new-markets.html"
+ "distribute/users/expand-to-new-markets.html",
+ "distribute/users/promote-with-ads.html"
]
},
"distribute/engagelanding": {
@@ -60,6 +61,7 @@
"distribute/engage/easy-signin.html",
"distribute/engage/deep-linking.html",
"distribute/engage/game-services.html",
+ "distribute/engage/analytics.html",
"distribute/engage/app-updates.html",
"distribute/engage/community.html",
"distribute/engage/video.html"
@@ -214,6 +216,13 @@
"distribute/stories/localization.html"
]
},
+ "distribute/users/promotewithads": {
+ "title": "",
+ "resources": [
+ "http://www.google.com/ads/admob/#subid=us-en-et-dac",
+ "distribute/essentials/optimizing-your-app.html"
+ ]
+ },
"distribute/users/buildbuzz": {
"title": "",
"resources": [
@@ -394,6 +403,14 @@
"http://play.google.com/about/developer-content-policy.html"
]
},
+ "distribute/engage/analytics": {
+ "title": "",
+ "resources": [
+ "http://www.google.com/analytics/mobile/",
+ "http://android-developers.blogspot.com/2013/10/improved-app-insight-by-linking-google.html",
+ "https://developers.google.com/analytics/devguides/collection/android/"
+ ]
+ },
"distribute/engage/widgets": {
"title": "",
"resources": [
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index f26b747b..d8db5bf 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -726,6 +726,21 @@
"tags": [
"#engagement",
],
+ "url": "http://www.google.com/analytics/mobile/",
+ "timestamp": 1383243492000,
+ "image": "http://www.google.com//analytics/images/heros/mobile-index.jpg",
+ "title": "Google Mobile App Analytics",
+ "summary": "Mobile App Analytics measures what matters most at all key stages: from first discovery and download to in-app purchases. ",
+ "keywords": ["analytics,user behavior"],
+ "type": "guide",
+ "titleFriendly": ""
+ },
+ {
+ "lang": "en",
+ "group": "",
+ "tags": [
+ "#engagement",
+ ],
"url": "https://developers.google.com/app-indexing/",
"timestamp": 1383243492000,
"image": "https://developers.google.com/app-indexing/images/allthecooks_srp.png",
@@ -1101,4 +1116,17 @@
"type": "Google+",
"titleFriendly": ""
},
+ {
+ "lang": "en",
+ "group": "",
+ "tags": ["analytics"],
+ "url": "https://developers.google.com/analytics/devguides/collection/android/",
+ "timestamp": null,
+ "image": "https://developers.google.com/analytics/images/home/gear-logo.png",
+ "title": "Google Mobile App Analytics SDK",
+ "summary": "The Google Analytics for Mobile Apps SDKs make it easy for you to implement Google Analytics in your mobile application.",
+ "keywords": ["analytics, user behavior"],
+ "type": "sdk",
+ "titleFriendly": ""
+ }
]);
\ No newline at end of file
diff --git a/docs/html/sdk/exploring.jd b/docs/html/sdk/exploring.jd
index 7749060..b34c1cf 100644
--- a/docs/html/sdk/exploring.jd
+++ b/docs/html/sdk/exploring.jd
@@ -5,163 +5,6 @@
@jd:body
-<p>The Android SDK is composed of modular packages that you can download separately using
-the Android SDK Manager. For example, when the SDK Tools are updated or a new version of
-the Android platform is released, you can use the SDK Manager to quickly download them to
-your environment. Simply follow the procedures described in <a
-href="{@docRoot}sdk/installing/adding-packages.html">Adding Platforms and Packages</a>.</p>
-
-<p>There are several different packages available for the Android SDK. The table below describes
-most of the available packages and where they're located once you download them.</p>
-
-
-<h2 id="Packages">Available Packages</h2>
-
-
-<table>
- <tr><th>Package</th><th>Description</th><th>File Location</th></tr>
- <tr>
- <td><a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools</a></td>
- <td>Contains tools for debugging and testing, plus other
-utilities that are required to develop an app. If you've just installed the SDK starter package,
-then you already have the latest version of this package. Make sure you keep this up to date.</td>
- <td>{@code <sdk>/tools/}</td></tr>
- <tr><td>SDK Platform-tools</td>
- <td>Contains platform-dependent tools for developing and debugging
-your application. These tools support the latest features of the Android platform and are typically
-updated only when a new platform becomes available. These tools are always backward compatible with
-older platforms, but you must be sure that you have the latest version of these tools when you
-install a new SDK platform.</td>
- <td>{@code <sdk>/platform-tools/}</td>
- </tr>
-
- <tr>
- <td>Documentation</td>
- <td>An offline copy of the latest documentation for the Android
-platform APIs.</td>
- <td>{@code <sdk>/docs/}</td>
- </tr>
- <tr><td>SDK Platform</td>
- <td>There's one SDK Platform available for each version of Android. It includes an {@code
-android.jar} file with a fully compliant Android library. In order to build an Android app, you must
-specify an SDK platform as your build target.</td>
- <td>{@code <sdk>/platforms/<android-version>/}</td>
- </tr>
- <tr>
- <td>System Images</td>
- <td>Each platform version offers one or more different system images (such as for ARM
-and x86). The Android emulator requires a system image to operate. You should always test your
-app on the latest version of Android and using the emulator with the latest system image is a
-good way to do so.</td>
- <td>{@code <sdk>/platforms/<android-version>/}</td>
- </tr>
- <tr>
- <td>Sources for Android SDK</td>
- <td>A copy of the Android platform source code that's useful for
-stepping through the code while debugging your app.</td>
- <td>{@code <sdk>/sources/}</td>
- </tr>
- <tr>
- <td><a href="{@docRoot}tools/samples/index.html">Samples for SDK</a></td>
- <td>A collection of sample apps that demonstrate a variety of the
-platform APIs. These are a great resource to browse Android app code. The API Demos app in
-particular provides a huge number of small demos you should explore.</td>
- <td>{@code <sdk>/platforms/<android-version>/samples/}</td>
- </tr>
- <tr>
- <td><a href="http://developers.google.com/android">Google APIs</a></td>
- <td>An SDK add-on that provides both a platform you can use to develop an app
-using special Google APIs and a system image for the emulator so you can test your app using the
-Google APIs.</td>
- <td>{@code <sdk>/add-ons/}</td>
- </tr>
-
- <tr>
- <td><a href="{@docRoot}tools/support-library/index.html">Android Support</a></td>
- <td>A static library you can include in your app sources in order to use powerful
-APIs that aren't available in the standard platform. For example, the support library
-contains versions of the {@link android.support.v4.app.Fragment} class that's compatible with
-Android 1.6 and higher (the class was originally introduced in Android 3.0) and the {@link
-android.support.v4.view.ViewPager} APIs that allow you to easily build a side-swipeable UI.</td>
- <td>{@code <sdk>/extras/android/support/}</td>
- </tr>
- <tr>
- <td><a href="{@docRoot}google/play/billing/index.html">Google Play Billing</a></td>
- <td>Provides the static libraries and samples that allow you to
-integrate billing services in your app with Google Play.</td>
- <td>{@code <sdk>/extras/google/}</td>
- </tr>
- <tr>
- <td><a href="{@docRoot}google/play/licensing/index.html">Google Play Licensing</a></td>
- <td>Provides the static libraries and samples that allow you to perform license verification for
-your app when distributing with Google Play.</td>
- <td>{@code <sdk>/extras/google/}</td>
- </tr>
-</table>
-
-<p>The above table is not comprehensive and you can <a
-href="#AddingSites">add new sites</a> to download additional packages from third-parties.</p>
-
-<p>In some cases, an SDK package may require a specific minimum revision of
-another package or SDK tool. For example, there may be a dependency between the ADT Plugin for
-Eclipse and
-the SDK Tools package. When you install the SDK Tools
-package, you should also upgrade to the required version of ADT (if you
-are developing in Eclipse). In this case, the major version number for your ADT plugin should
-always match the revision number of your SDK Tools (for example, ADT 8.x requires SDK Tools r8).
-</p>
-
-<p>The development tools will notify you with debug warnings if there is dependency that you need to
-address. The Android SDK Manager also enforces dependencies by requiring that you download any
-packages that are needed by those you have selected.</p>
-
-
-
-
-
-<h2 id="AddingSites">Adding New Sites</h2>
-
-<p>By default, <strong>Available Packages</strong> displays packages available from the
-<em>Android Repository</em> and <em>Third party Add-ons</em>. You can add other sites that host
-their own Android SDK add-ons, then download the SDK add-ons
-from those sites.</p>
-
-<p>For example, a mobile carrier or device manufacturer might offer additional
-API libraries that are supported by their own Android-powered devices. In order
-to develop using their libraries, you must install their Android SDK add-on, if it's not already
-available under <em>Third party Add-ons</em>. </p>
-
-<p>If a carrier or device manufacturer has hosted an SDK add-on repository file
-on their web site, follow these steps to add their site to the Android SDK
-Manager:</p>
-
-<ol>
- <li>Select <strong>Available Packages</strong> in the left panel.</li>
- <li>Click <strong>Add Add-on Site</strong> and enter the URL of the
-<code>repository.xml</code> file. Click <strong>OK</strong>.</li>
-</ol>
-<p>Any SDK packages available from the site will now be listed under a new item named
-<strong>User Add-ons</strong>.</p>
-
-
-
-
-<h2 id="troubleshooting">Troubleshooting</h2>
-
-<p><strong>Problems connecting to the SDK repository</strong></p>
-
-<p>If you are using the Android SDK Manager to download packages and are encountering
-connection problems, try connecting over http, rather than https. To switch the
-protocol used by the Android SDK Manager, follow these steps: </p>
-
-<ol>
- <li>With the Android SDK Manager window open, select "Settings" in the
- left pane. </li>
- <li>On the right, in the "Misc" section, check the checkbox labeled "Force
- https://... sources to be fetched using http://..." </li>
- <li>Click <strong>Save & Apply</strong>.</li>
-</ol>
-
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index e7f78e8..c6dbb90 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -206,12 +206,10 @@
<div id="next-steps" style="display:none;position:absolute;width:inherit">
- <p>Now that you've downloaded the Android SDK, you don't need to return here
- for SDK updates. The SDK tools allow you to
- install additional packages and future updates from the SDK Manager.</p>
- <p>For instructions about setting up your Android SDK for the first time,
- read <a id="next-link" href="{@docRoot}sdk/installing/bundle.html">Setting
- Up the ADT Bundle</a>.</p>
+ <p>You're just a few steps away from building apps for Android!</p>
+ <p>In a moment, you'll be redirected to <a
+ id="next-link" href="{@docRoot}sdk/installing/index.html">Installing the
+ Android SDK</a>.</p>
</div><!-- end next-steps -->
diff --git a/docs/html/sdk/installing/adding-packages.jd b/docs/html/sdk/installing/adding-packages.jd
index bba936e..c38c927 100644
--- a/docs/html/sdk/installing/adding-packages.jd
+++ b/docs/html/sdk/installing/adding-packages.jd
@@ -1,63 +1,222 @@
-page.title=Adding Platforms and Packages
+page.title=Adding SDK Packages
@jd:body
+<style>
+ol.large {
+ margin-left:0;
+}
+ol.large > li {
+ list-style-position: inside;
+ list-style-type:none;
+ margin:30px 0 0 0;
+ padding:30px 20px;
+ background:#eee;
+}
+ol.large > li:nth-child(odd) {
+}
+ol.large > li:before {
+ display:inline;
+ left:-40px;
+ float:left;
+ width:20px;
+ font-size:20px;
+ line-height:20px;
+}
+ol.large > li > h2 {
+ font-size:20px;
+ line-height:20px;
+ padding:0 0 0 20px;
+ margin:0 0 20px 0;
+ display:inline-block;
+ font-weight:normal;
+}
+ol.large > li:nth-child(1):before {
+ content:"1. ";
+}
+ol.large > li:nth-child(2):before {
+ content:"2. ";
+}
+ol.large > li:nth-child(3):before {
+ content:"3. ";
+}
+ol.large > li:nth-child(4):before {
+ content:"4. ";
+}
+ol.large > li:nth-child(5):before {
+ content:"5. ";
+}
+ol.large > li:nth-child(6):before {
+ content:"6. ";
+}
+</style>
-<p>The Android SDK separates tools, platforms, and other components into packages you can
- download using the Android SDK Manager. The original
-SDK package you've downloaded includes only the SDK Tools. To develop an Android app,
-you also need to download at least one Android platform and the latest SDK Platform-tools.</p>
-<ol>
-<li>Launch the SDK Manager.
-<p>If you've used the Windows installer to install the SDK tools, you should already have the
-Android SDK Manager open. Otherwise, you can launch the Android SDK Manager in one of the following
-ways:</p>
+<p>
+By default, the Android SDK does not include everything you need to start developing.
+The SDK separates tools, platforms, and other components into packages you can
+download as needed using the
+<a href="{@docRoot}tools/help/sdk-manager.html">Android SDK Manager</a>.
+So before you can start, there are a few packages you should add to your Android SDK.</p>
+
+<p>To start adding packages, launch the Android SDK Manager in one of the following ways:</p>
<ul>
- <li>On Windows, double-click the <code>SDK Manager.exe</code> file at the root of the Android
-SDK directory.</li>
- <li>On Mac or Linux, open a terminal and navigate to the <code>tools/</code> directory in the
-Android SDK, then execute <code>android sdk</code>.</li>
+ <li>In Eclipse or Android Studio, click <strong>SDK Manager</strong>
+<img src="{@docRoot}images/tools/sdk-manager-studio.png"
+style="vertical-align:bottom;margin:0;height:17px" /> in the toolbar.</li>
+ <li>If you're not using Eclipse or Android Studio:
+ <ul>
+ <li>Windows: Double-click the <code>SDK Manager.exe</code> file at the root of the Android
+ SDK directory.</li>
+ <li>Mac/Linux: Open a terminal and navigate to the <code>tools/</code> directory in the
+ Android SDK, then execute <code>android sdk</code>.</li>
+ </ul>
+ </li>
</ul>
+
+<ol class="large">
+<li>
+ <h2 class="norule">Get the latest SDK tools</h2>
+
+<img src="/images/sdk_manager_packages.png" alt="" width="350" style="float:right;margin-left:20px" />
+
+ <p>As a minimum when setting up the Android SDK,
+ you should download the latest tools and Android platform:</p>
+ <ol>
+ <li>Open the Tools directory and select:
+ <ul>
+ <li><strong>Android SDK Tools</strong></li>
+ <li><strong>Android SDK Platform-tools</strong></li>
+ <li><strong>Android SDK Build-tools</strong></li>
+ </ul>
+ </li>
+ <li>Open the first Android X.X folder (the latest version) and select:
+ <ul>
+ <li><strong>SDK Platform</strong></li>
+ <li>A system image for the emulator, such as <br>
+ <strong>ARM EABI v7a System Image</strong></li>
+ </ul>
+ </li>
+ <li>Click <strong>Install</strong>.</li>
+ </ol>
</li>
-<li>The SDK Manager shows all the SDK packages available for you to add to your Android SDK.
-As a minimum configuration for your SDK, we recommend you install the following:
-<ul>
- <li>The latest Tools packages (check the <strong>Tools</strong> folder).</li>
- <li>The latest version of Android (check the first <strong>Android</strong> folder).</li>
- <li>The Android Support Library (open the <strong>Extras</strong> folder and check
- <strong>Android Support Library</strong>).</li>
-</ul>
+<li>
+ <h2 class="norule">Get the support library for additional APIs</h2>
-<p>Once you've chosen your packages, click <strong>Install</strong>. The Android SDK Manager
-installs the selected packages into your Android SDK environment.</li>
+ <div class="sidebox">
+ <h3>Why use the support library?</h3>
+
+ <p>The support library is required for:</p>
+ <ul>
+ <li><a href="{@docRoot}wear/index.html">Android Wear</a></li>
+ <li><a href="{@docRoot}tv/index.html">Android TV</a></li>
+ <li><a href="{@docRoot}google/play-services/cast.html">Google Cast</a></li>
+ </ul>
+
+ <p>The support library also provides these popular APIs:</p>
+ <ul>
+ <li><a href="{@docRoot}reference/android/support/v4/widget/DrawerLayout.html">Navigation
+ drawer</a></li>
+ <li><a href="{@docRoot}reference/android/support/v4/view/ViewPager.html">Swipe views</a></li>
+ <li><a href="{@docRoot}reference/android/support/v7/app/ActionBar.html">Backward-compatible
+ action bar</a></li>
+ </ul>
+ </div>
+
+ <p>The <a href="{@docRoot}tools/support-library/features.html">Android Support Library</a>
+ provides an extended set of APIs that are compatible with most versions of Android.</p>
+
+ <p>To download the support library:</p>
+ <ol>
+ <li>Open the <strong>Extras</strong> directory and select:
+ <ul>
+ <li><strong>Android Support Repository</strong></li>
+ <li><strong>Android Support Library</strong></li>
+ </ul>
+ </li>
+ <li>Click <strong>Install</strong>.</li>
+ </ol>
+
+ <p> </p>
+ <p> </p>
+
+</li>
+
+
+<li>
+ <h2 class="norule">Get Google Play services for even more APIs</h2>
+
+ <div class="sidebox">
+ <h3>Why use Google Play services?</h3>
+
+ <p>The Google Play services APIs provide a variety of features and services for your Android
+ apps, such as:</p>
+ <ul>
+ <li><a href="{@docRoot}google/play-services/plus.html">User authentication</a></li>
+ <li><a href="{@docRoot}google/play-services/maps.html">Google Maps</a></li>
+ <li><a href="{@docRoot}google/play-services/cast.html">Google Cast</a></li>
+ <li><a href="{@docRoot}google/play-services/games.html">Games achievements and
+ leaderboards</a></li>
+ <li><a href="{@docRoot}google/play-services/index.html">And much more</a></li>
+ </ul>
+ </div>
+
+ <p>To develop with Google APIs, you need the Google Play services package:</p>
+ <ol>
+ <li>Open the <strong>Extras</strong> directory and select:
+ <ul>
+ <li><strong>Google Repository</strong></li>
+ <li><strong>Google Play services</strong></li>
+ </ul>
+ </li>
+ <li>Click <strong>Install</strong>.</li>
+ </ol>
+
+ <p class="note"><strong>Note:</strong> Google Play services APIs are not available on all
+ Android-powered devices, but are available on all devices with Google Play Store. To use these
+ APIs in the Android emulator, you must also install the the <strong>Google APIs</strong>
+ system image from the latest Android X.X directory in the SDK Manager.</p>
+</li>
+
+
+
+<li>
+ <h2 class="norule">Build something!</h2>
+
+<p>With the above packages now in your Android SDK, you're ready to build apps
+for Android. As new tools and other APIs become available, simply launch the SDK Manager
+ to download the new packages for your SDK.</p>
+
+<p>Here are a few options for how you should proceed:</p>
+
+<div class="cols" style="padding:10px 0">
+<div class="col-4">
+<h3>Get started</h3>
+<p>If you're new to Android development, learn the basics of Android apps by following
+the guide to <strong><a href="{@docRoot}training/basics/firstapp/index.html"
+>Building Your First App</a></strong>.</p>
+
+</div>
+<div class="col-4 box">
+<h3>Build for wearables</h3>
+<p>If you're ready to start building apps for Android wearables, see the guide to
+<strong><a href="{@docRoot}wear/preview/start.html">Building Apps for Android Wear</a></strong>.</p>
+
+</div>
+<div class="col-4 box">
+<h3>Use Google APIs</h3>
+<p>To start using Google APIs, such as Maps or
+Play Game services, see the guide to
+<strong><a href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services
+APIs</a></strong>.</p>
+
+</div>
+</div><!-- end cols -->
+
+
+</li>
+
</ol>
-<p>With these packages installed, you're ready to start developing.
-To get started, read <a href="{@docRoot}training/basics/firstapp/index.html"
->Building Your First App</a>.</p>
-
-<img src="/images/sdk_manager_packages.png" alt="" height="396" />
-<p class="img-caption"><strong>Figure 1.</strong> The Android SDK Manager shows the
-SDK packages that are available, already installed, or for which an update is available.</p>
-
-
-
-<h3>Additional information</h3>
-
-<ul>
- <li>For more information about using the SDK Manager and some of the available packages,
-see the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> document.</li>
- <li>This web site provides all information you need to develop Android apps, including <a
-href="{@docRoot}design/index.html">design guidelines</a>,
-<a href="{@docRoot}training/index.html">developer training</a>, <a
-href="{@docRoot}reference/packages.html">API reference</a>, and information
-about how you can <a href="{@docRoot}distribute/index.html">distribute your app</a>. We recommend
-you begin by reading <a href="{@docRoot}training/basics/firstapp/index.html"
->Building Your First App</a>.</li>
- <li>For additional resources about developing and distributing your app, see the
-<a href="{@docRoot}support.html">Developer Support Resources</a>.</li>
-</ul>
-
diff --git a/docs/html/sdk/installing/bundle.jd b/docs/html/sdk/installing/bundle.jd
index 1f7da55..22bdd11 100644
--- a/docs/html/sdk/installing/bundle.jd
+++ b/docs/html/sdk/installing/bundle.jd
@@ -1,45 +1,3 @@
page.title=Setting Up the ADT Bundle
@jd:body
-
-
-<p>The ADT Bundle provides everything you need to start developing apps, including
-a version of the Eclipse IDE with built-in <b>ADT (Android Developer Tools)</b> to
-streamline your Android app development.
-If you haven't already, go download the <a href="{@docRoot}sdk/index.html"
->Android ADT Bundle</a>. (If you downloaded the SDK Tools only, for use with an
-existing IDE, you should instead read
-<a href="{@docRoot}sdk/installing/index.html">Setting Up an Existing IDE</a>.)</p>
-
-<h3>Install the SDK and Eclipse IDE</h3>
-<ol>
-<li>Unpack the ZIP file
-(named {@code adt-bundle-<os_platform>.zip}) and save it to an appropriate location,
-such as a "Development" directory in your home directory.</li>
-<li>Open the {@code adt-bundle-<os_platform>/eclipse/} directory and launch
-<strong>eclipse</strong>.</li>
-</ol>
-
-<p>That's it! The IDE is already loaded with the Android Developer Tools plugin and
-the SDK is ready to go. To start developing, read <a href="{@docRoot}training/basics/firstapp/index.html"
->Building Your First App</a>.</p>
-
-<p class="caution"><strong>Caution:</strong> Do not move any of the files or directories
-from the {@code adt-bundle-<os_platform>} directory. If you move the {@code eclipse}
-or {@code sdk} directory, ADT will not be able to locate the SDK and you'll
-need to manually update the ADT preferences.</p>
-
-<h3>Additional information</h3>
-
-<p>As you continue developing apps, you may need to install additional versions
-of Android for the emulator and other packages such as the library for
-Google Play In-app Billing. To install more packages, use
-the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>.</p>
-
-<p>Everything you need to develop Android apps is on this web site, including <a
-href="{@docRoot}design/index.html">design guidelines</a>,
-<a href="{@docRoot}training/index.html">developer training</a>, <a
-href="{@docRoot}reference/packages.html">API reference</a>, and information
-about how you can <a href="{@docRoot}distribute/index.html">distribute your app</a>.
-For additional resources about developing and distributing your app, see the
-<a href="{@docRoot}support.html">Developer Support Resources</a>.</p>
\ No newline at end of file
diff --git a/docs/html/sdk/installing/index.jd b/docs/html/sdk/installing/index.jd
index 6b63ba7..b6929bd 100644
--- a/docs/html/sdk/installing/index.jd
+++ b/docs/html/sdk/installing/index.jd
@@ -1,19 +1,213 @@
-page.title=Setting Up an Existing IDE
+page.title=Installing the Android SDK
@jd:body
-
-<p>You should have already downloaded the <a href="{@docRoot}sdk/index.html#ExistingIDE"
->Android SDK Tools</a>. (If you downloaded the ADT Bundle, you should instead read
-<a href="{@docRoot}sdk/installing/bundle.html">Setting Up the ADT Bundle</a>.)</p>
-
-<p>The SDK Tools package is not the complete SDK environment. It includes only the core SDK tools, which you can
-use to download the rest of the SDK packages (such as the latest system image).</p>
+<style>
+.paging-links {
+ margin:0 0 80px;
+}
+.paging-links .next-page-link {
+ right:initial;
+}
+.procedure-box {
+ padding:20px 20px 5px;
+ margin-bottom:1em;
+ background:#eee;
+}
+</style>
-<div id="win" class="docs" style="display:none">
-<h3>Getting started on Windows</h3>
+<!-- ################### ADT BUNDLE ####################### -->
+<div id="adt" heading="Installing the Eclipse ADT Bundle" style="display:none">
+
+
+<p>The Eclipse ADT Bundle provides everything you need to start developing apps, including
+the Android SDK tools and a version of the Eclipse IDE with built-in ADT
+(Android Developer Tools) to streamline your Android app development.</p>
+
+<p>If you didn't download the Eclipse ADT bundle, go <a href="{@docRoot}sdk/index.html"
+><b>download the Eclipse ADT bundle now</b></a>, or switch to the
+<a href="{@docRoot}sdk/installing/index.html?pkg=studio">Android Studio
+install</a> or <a href="{@docRoot}sdk/installing/index.html?pkg=tools">stand-alone SDK Tools
+install</a> instructions</i>.</p>
+
+<div class="procedure-box">
+<p><b>To set up the ADT Bundle:</b></p>
+<ol>
+<li>Unpack the ZIP file
+(named {@code adt-bundle-<os_platform>.zip}) and save it to an appropriate location,
+such as a "Development" directory in your home directory.</li>
+<li>Open the {@code adt-bundle-<os_platform>/eclipse/} directory and launch
+<strong>Eclipse</strong>.</li>
+</ol>
+
+<p class="caution"><strong>Caution:</strong> Do not move any of the files or directories
+from the {@code adt-bundle-<os_platform>} directory. If you move the {@code eclipse/}
+or {@code sdk/} directory, ADT will not be able to locate the SDK and you'll
+need to manually update the ADT preferences.</p>
+</div>
+
+<p>Eclipse with ADT is now ready and loaded with the Android developer tools, but there are still
+a couple packages you should add to make your Android SDK complete.</p>
+
+<p class="paging-links">
+<a href="{@docRoot}sdk/installing/adding-packages.html" class="next-page-link">
+Continue: Adding SDK Packages</a></p>
+
+
+</div>
+<!-- ################ END ADT BUNDLE ##################### -->
+
+
+
+
+
+
+<!-- ################ STUDIO ##################### -->
+<div id="studio" heading="Installing Android Studio" style="display:none">
+
+<p>Android Studio provides everything you need to start developing apps, including
+the Android SDK tools and the Android Studio IDE (powered by IntelliJ) to
+streamline your Android app development.</p>
+
+<p>If you didn't download Android Studio, go <a href="{@docRoot}sdk/installing/studio.html"
+><b>download Android Studio now</b></a>, or switch to the
+<a href="{@docRoot}sdk/installing/index.html?pkg=adt">Eclipse ADT
+install</a> or <a href="{@docRoot}sdk/installing/index.html?pkg=tools">stand-alone SDK Tools
+install</a> instructions.</p>
+
+
+<p>Before you set up Android Studio, be sure you have installed
+JDK 6 or greater (the JRE alone is not sufficient). To check if you
+have JDK installed (and which version), open a terminal and type <code>javac -version</code>.
+If the JDK is not available or the version is lower than 6,
+<a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" class="external-link"
+>go download JDK</a>.</p>
+
+
+<div class="procedure-box">
+
+<p id="instructions-toggle"
+style="float:right;font-size:13px"><a href='' onclick='showAll();return false;'
+>[ Show instructions for all platforms ]</a></p>
+
+<div class="win docs" style="display:none">
+
+<p><b>To set up Android Studio on Windows:</b></p>
+ <ol>
+ <li>Launch the downloaded EXE file, {@code android-studio-bundle-<version>.exe}.</li>
+ <li>Follow the setup wizard to install Android Studio.
+
+ <p>On some Windows systems, the launcher script does not find where Java is installed.
+ If you encounter this problem,
+ you need to set an environment variable indicating the correct location.</p>
+ <p>Select <strong>Start menu > Computer > System Properties >
+ Advanced System Properties</strong>. Then open <strong>Advanced tab > Environment
+ Variables</strong> and add a new system variable <code>JAVA_HOME</code> that points to
+ your JDK folder, for example <code>C:\Program Files\Java\jdk1.7.0_21</code>.</p>
+ </p>
+ </li>
+
+ </ol>
+
+
+<p>The individual tools and
+other SDK packages are saved within the Android Studio application directory.
+To access the tools directly, use a terminal to navigate into the application and locate
+the {@code sdk/} directory. For example:</p>
+<p><code>\Users\<user>\AppData\Local\Android\android-studio\sdk\</code></p>
+
+
+
+</div><!-- end windows -->
+
+
+<div class="mac docs" style="display:none">
+
+<p><b>To set up Android Studio on Mac OSX:</b></p>
+ <ol>
+ <li>Open the downloaded DMG file, {@code android-studio-bundle-<version>.dmg}.</li>
+ <li>Drag and drop Android Studio into the Applications folder.
+ <p>
+ Depending on your security settings, when you attempt to open Android Studio, you might
+ see a warning that says the package is damaged and should be moved to the trash. If this
+ happens, go to <strong>System Preferences > Security & Privacy</strong> and under
+ <strong>Allow applications downloaded from</strong>, select <strong>Anywhere</strong>.
+ Then open Android Studio again.</p>
+ </li>
+ </ol>
+
+<p>The individual tools and
+other SDK packages are saved within the Android Studio application directory.
+To access the tools directly, use a terminal to navigate into the application and locate
+the {@code sdk/} directory. For example:</p>
+<p><code>/Applications/Android\ Studio.app/sdk/</code></p>
+
+
+</div><!-- end mac -->
+
+
+<div class="linux docs" style="display:none">
+
+<p><b>To set up Android Studio on Linux:</b></p>
+
+ <ol>
+ <li>Unpack the downloaded Tar file, {@code android-studio-bundle-<version>.tgz}, into an appropriate
+ location for your applications.
+ <li>To launch Android Studio, navigate to the {@code android-studio/bin/} directory
+ in a terminal and execute {@code studio.sh}.
+ <p>You may want to add {@code android-studio/bin/} to your PATH environmental
+ variable so that you can start Android Studio from any directory.</p>
+ </li>
+ </ol>
+
+</div><!-- end linux -->
+</div><!-- end procedure box -->
+
+<p>Android Studio is now ready and loaded with the Android developer tools, but there are still a
+couple packages you should add to make your Android SDK complete.</p>
+
+<p class="paging-links">
+<a href="{@docRoot}sdk/installing/adding-packages.html" class="next-page-link">
+Continue: Adding SDK Packages</a></p>
+
+
+</div>
+<!-- ################ END STUDIO ##################### -->
+
+
+
+
+
+
+
+
+
+<!-- ################ JUST SDK TOOLS ##################### -->
+<div id="tools" heading="Installing the Stand-alone SDK Tools" style="display:none">
+
+
+<p>The stand-alone SDK Tools package does not include a complete Android development environment.
+It includes only the core SDK tools, which you can access from a command line or with a plugin
+for your favorite IDE (if available).</p>
+
+<p>If you didn't download the SDK tools, go <a href="{@docRoot}sdk/index.html"
+><b>download the SDK now</b></a>,
+or switch to the <a href="{@docRoot}sdk/installing/index.html?pkg=adt">Eclipse ADT
+install</a> or <a href="{@docRoot}sdk/installing/index.html?pkg=studio">Android Studio
+install</a> instructions.</p>
+
+
+<div class="procedure-box">
+<p id="instructions-toggle"
+style="float:right;font-size:13px"><a href='' onclick='showAll();return false;'
+>[ Show instructions for all platforms ]</a></p>
+
+<div class="win docs" style="display:none">
+
+<p><b>To get started on Windows:</b></p>
+
<p>Your download package is an executable file that starts an installer. The installer checks your machine
for required tools, such as the proper Java SE Development Kit (JDK) and installs it if necessary.
The installer then saves the Android SDK Tools into a default location (or you can specify the location).</p>
@@ -21,15 +215,9 @@
<ol>
<li>Double-click the executable ({@code .exe} file) to start the install.</li>
<li>Make a note of the name and location in which it saves the SDK on your system—you will need to
-refer to the SDK directory later, when setting up the ADT plugin and when using
+refer to the SDK directory later when using
the SDK tools from the command line.</li>
-<li>Once the installation completes, the installer offers to start the Android SDK Manager.
-If you'll be using Eclipse, <strong>do not</strong> start the Android SDK Manager,
-and instead move on to <a href="{@docRoot}sdk/installing/installing-adt.html"
->Installing the Eclipse Plugin</a>.
-<p>If you're using a different IDE,
-start the SDK Manager and read <a href="{@docRoot}sdk/installing/adding-packages.html"
->Adding Platforms and Packages</a>.</p>
+<li>Once the installation completes, the installer starts the Android SDK Manager.
</li>
</ol>
@@ -37,51 +225,37 @@
-<div id="mac" class="docs" style="display:none">
+<div class="mac docs" style="display:none">
-<h3>Getting started on Mac</h3>
+<p><b>To get started on Mac OSX:</b></p>
-<ol>
-<li>Unpack the ZIP file you've downloaded. By default, it's unpacked
+<p>Unpack the ZIP file you've downloaded. By default, it's unpacked
into a directory named <code>android-sdk-mac_x86</code>. Move it to an appropriate location on your machine,
-such as a "Development" directory in your home directory.
+such as a "Development" directory in your home directory.</p>
<p>Make a note of the name and location of the SDK directory on your system—you will need to
-refer to the SDK directory later, when setting up the ADT plugin and when using
+refer to the SDK directory later when using
the SDK tools from the command line.</p>
-</li>
-<li>If you're using Eclipse, move on to <a href="{@docRoot}sdk/installing/installing-adt.html"
->Installing the Eclipse Plugin</a>. Otherwise, if you're using a different IDE,
-read <a href="{@docRoot}sdk/installing/adding-packages.html"
->Adding Platforms and Packages</a>.</li>
-</ol>
</div>
-<div id="linux" class="docs" style="display:none">
+<div class="linux docs" style="display:none">
-<h3>Getting started on Linux</h3>
+<p><b>To get started on Linux:</b></p>
-<ol>
-<li>Unpack the {@code .tgz} file you've downloaded. By default, the SDK files are unpacked
+<p>Unpack the {@code .tgz} file you've downloaded. By default, the SDK files are unpacked
into a directory named <code>android-sdk-linux_x86</code>. Move it to an appropriate location on your machine,
-such as a "Development" directory in your home directory.
+such as a "Development" directory in your home directory.</p>
<p>Make a note of the name and location of the SDK directory on your system—you will need to
-refer to the SDK directory later, when setting up the ADT plugin and when using
+refer to the SDK directory later when using
the SDK tools from the command line.</p>
-</li>
-<li>If you're using Eclipse, move on to <a href="{@docRoot}sdk/installing/installing-adt.html"
->Installing the Eclipse Plugin</a>. Otherwise, if you're using a different IDE,
-read <a href="{@docRoot}sdk/installing/adding-packages.html"
->Adding Platforms and Packages</a>.</li>
-</ol>
-<h5 id="Troubleshooting"><a href='' class="expandable"
+<h5 id="Troubleshooting" style="margin-bottom:15px"><a href='' class="expandable"
onclick="toggleExpandable(this,'#ubuntu-trouble');return false;"
>Troubleshooting Ubuntu</a></h5>
@@ -122,38 +296,129 @@
</div><!-- end ubuntu trouble -->
+</div><!-- end linux -->
+</div><!-- end procedure box -->
+
+
+<p>The Android SDK tools are now ready to begin developing apps, but there are still a
+couple packages you should add to make your Android SDK complete.</p>
+
+<p class="paging-links">
+<a href="{@docRoot}sdk/installing/adding-packages.html" class="next-page-link">
+Continue: Adding SDK Packages</a></p>
+
+
+</div>
+<!-- ################ END JUST TOOLS ##################### -->
+
+
+
+
+
+<!-- ################ DEFAULT ##################### -->
+<style>
+h3.large-link {
+ display:inline-block;
+ width:100%;
+ text-align:center;
+ border:1px solid #258aaf;
+ padding:10px 0;
+ color:inherit;
+}
+</style>
+
+<div id="default" style="display:none">
+
+<p>If you haven't already done so, <b><a href="{@docRoot}sdk/index.html">click here to download
+the Android SDK</a></b>. </p>
+
+<p>Otherwise, select which SDK package you want to install:</p>
+
+<div class="cols" style="margin-bottom:60px">
+<div class="col-4">
+<a href="{@docRoot}sdk/installing/index.html?pkg=adt">
+<h3 class="large-link">Eclipse ADT</h3>
+</a>
</div>
-<p style="margin-top:2em;"><a href='' onclick='showAll();return false;'>Information for other platforms</a></p>
+<div class="col-4">
+<a href="{@docRoot}sdk/installing/index.html?pkg=studio">
+<h3 class="large-link">Android Studio</h3></a>
+</div>
+
+<div class="col-4">
+<a href="{@docRoot}sdk/installing/index.html?pkg=tools">
+<h3 class="large-link">Stand-alone SDK Tools</h3></a>
+</div>
+</div>
+
+
+</div>
+<!-- ################ END DEFAULT ##################### -->
+
+
+
+
+
<script>
- var $osDocs;
- if (navigator.appVersion.indexOf("Win")!=-1) {
- $osDocs = $('#win');
- } else if (navigator.appVersion.indexOf("Mac")!=-1) {
- $osDocs = $('#mac');
- } else if (navigator.appVersion.indexOf("Linux")!=-1) {
- $osDocs = $('#linux');
- }
- if ($osDocs.length) {
- // reveal only the docs for this OS
- $osDocs.show();
- } else {
- // not running a compatible OS, so just show all the docs
- $('.docs').show();
- }
+// Show proper instructions based on downloaded SDK package
+var package = getUrlParam("pkg");
+if (package == "tools") {
+ // Show the SDK Tools (other IDE) instructions
+ $("h1").text($("#tools").attr('heading'));
+ $("#tools").show();
+} else if (package == "adt") {
+ // Show the ADT instructions
+ $("h1").text($("#adt").attr('heading'));
+ $("#adt").show();
+} else if (package == "studio") {
+ // Show the Android Studio instructions
+ $("h1").text($("#studio").attr('heading'));
+ $("#studio").show();
+} else {
+ // Show the default page content so user can select their setup
+ $("#default").show();
+}
- function showAll() {
- $('.docs').each(function() {
- if (!$(this).is(':visible')) {
- console.log('show')
- $(this).show();
- } else {
- console.log('hide')
- $(this).hide();
- $osDocs.show();
- }
- });
+// Show the proper instructions based on machine OS
+var $osDocs;
+if (navigator.appVersion.indexOf("Win")!=-1) {
+ $osDocs = $('.win');
+} else if (navigator.appVersion.indexOf("Mac")!=-1) {
+ $osDocs = $('.mac');
+} else if (navigator.appVersion.indexOf("Linux")!=-1) {
+ $osDocs = $('.linux');
+}
+
+if ($osDocs.length) {
+ // reveal only the docs for this OS
+ $osDocs.show();
+} else {
+ // not running a compatible OS, so just show all the docs
+ $('.docs').show();
+}
+
+
+/* Shows all the machine OS instructions */
+function showAll() {
+ $('.docs').show();
+ $("#instructions-toggle").hide();
+}
+
+/* Returns the value for the given URL parameter */
+function getUrlParam(param) {
+ var url = window.location.search.substring(1);
+ var variables = url.split('&');
+ for (var i = 0; i < variables.length; i++) {
+ var paramName = variables[i].split('=');
+ if (escape(paramName[0]) == param) {
+ return escape(paramName[1]);
+ }
}
+}
+
+
+
</script>
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index 7bf366c..b23212d 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -15,19 +15,16 @@
UI, debug your app, and export signed (or unsigned) app packages (APKs) for distribution.
</p>
-<p>If you need to install Eclipse, you can download it from <a href=
-"http://www.eclipse.org/downloads/">eclipse.org/downloads/</a>.</p>
-
-
-<p class="note"><strong>Note:</strong> If you prefer to work in a different IDE, you do not need to
-install Eclipse or ADT. Instead, you can directly use the SDK tools to build and
-debug your application.</p>
+<p class="note"><strong>Note:</strong> You should install the ADT plugin
+only if you already have an Eclipse installation that you want to continue using. If you do not
+have Eclipse installed, you should instead <b><a href="{@docRoot}sdk/index.html">install
+the complete Android SDK</a></b>, which includes the latest IDE for Android developers.</p>
<h2 id="Download">Download the ADT Plugin</h2>
-
+<p>To add the ADT plugin to Eclipse:</p>
<ol>
<li>Start Eclipse, then select <strong>Help</strong> > <strong>Install New
Software</strong>.</li>
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index a2c32f0..7127ba6 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -7,7 +7,7 @@
<div style="position:relative;min-height:660px;">
-<h3 style="color:#f80">EARLY ACCESS PREVIEW</h3>
+<h3 style="color:#f80">BETA RELEASE</h3>
<div id="tos" style="position:absolute;display:none;width:inherit;">
<div class="col-13" style="margin:0;"> </div><!-- provides top margin for content -->
@@ -183,8 +183,6 @@
-
-
<div id="main">
<div class="figure" style="width:400px;margin-top:-20px">
@@ -298,7 +296,7 @@
Check for updates</strong>) to see whether an update is available.</p>
<p>If an update is not available,
-follow the <a href="#Installing">installation instructions</a> below and replace your existing
+click the button above to download and replace your existing
installation.</p>
<div class="caution">
@@ -311,98 +309,6 @@
the Android SDK Manager.</p>
</div>
-
-<h2 id="Installing">Installing Android Studio</h2>
-<p>Android Studio requires JDK 6 or greater (JRE alone is not sufficient). To check if you
-have JDK installed (and which version), open a terminal and type <code>javac -version</code>.
-If JDK is not available or the version is lower than 6,
-<a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html">download
-JDK from here</a>.</p>
-<p>To install Android Studio:</p>
-<ol>
-<li>Download the <strong>Android Studio</strong> package from above.</li>
-<li>Install Android Studio and the SDK tools:
- <p><b>Windows:</b></p>
- <ol>
- <li>Launch the downloaded EXE file, {@code android-studio-bundle-<version>.exe}.</li>
- <li>Follow the setup wizard to install Android Studio.
-
- <div class="caution"><p><strong>Known issue:</strong>
- On some Windows systems, the launcher script does not find where Java is installed.
- If you encounter this problem,
- you need to set an environment variable indicating the correct location.</p>
- <p>Select <strong>Start menu > Computer > System Properties >
- Advanced System Properties</strong>. Then open <strong>Advanced tab > Environment
- Variables</strong> and add a new system variable <code>JAVA_HOME</code> that points to
- your JDK folder, for example <code>C:\Program Files\Java\jdk1.7.0_21</code>.</p>
- </div>
- </li>
-
- </ol>
- <p><b>Mac OS X:</b></p>
- <ol>
- <li>Open the downloaded DMG file, {@code android-studio-bundle-<version>.dmg}.</li>
- <li>Drag and drop Android Studio into the Applications folder.
-
- <div class="caution"><p><strong>Known issue:</strong>
- Depending on your security settings, when you attempt to open Android Studio, you might
- see a warning that says the package is damaged and should be moved to the trash. If this
- happens, go to <strong>System Preferences > Security & Privacy</strong> and under
- <strong>Allow applications downloaded from</strong>, select <strong>Anywhere</strong>.
- Then open Android Studio again.</p>
- </div>
- </li>
-
- </ol>
- <p><b>Linux:</b></p>
- <ol>
- <li>Unpack the downloaded Tar file, {@code android-studio-bundle-<version>.tgz}, into an appropriate
- location for your applications.
- <li>To launch Android Studio, navigate to the {@code android-studio/bin/} directory
- in a terminal and execute {@code studio.sh}.
- <p>You may want to add {@code android-studio/bin/} to your PATH environmental
- variable so that you can start Android Studio from any directory.</p>
- </li>
- </ol>
-</li>
-</ol>
-
-<p>That's it! You're ready to start developing apps with Android Studio.</p>
-
-<div class="note">
-<p><strong>Note:</strong> On Windows and Mac, the individual tools and
-other SDK packages are saved within the Android Studio application directory.
-To access the tools directly, use a terminal to navigate into the application and locate
-the {@code sdk/} directory. For example:</p>
-<p>Windows: <code>\Users\<user>\AppData\Local\Android\android-studio\sdk\</code></p>
-<p>Mac: <code>/Applications/Android\ Studio.app/sdk/</code></p>
-</div>
-
-<p>For a list of some known issues, see <a
-href="http://tools.android.com/knownissues">tools.android.com/knownissues</a>.</p>
-
-
-<h2 id="Start">Starting a Project</h2>
-
-<p>When you launch Android Studio for the first time, you'll see a Welcome
-screen that offers several ways to get started:</p>
-
-<ul>
- <li>To start building a new app, click <strong>New Project</strong>.
- <p>This starts the New Project wizard, which helps you set up a project using an app template.
- </li>
- <li>To import an existing Android app project, click <strong>Import Project</strong>.
- <p class="note"><strong>Note:</strong> If you previously developed your Android project
- with Eclipse, you should first use the new export feature in the ADT plugin to prepare
- your project with the new Gradle build system. For more information, read
- <a href="{@docRoot}sdk/installing/migrate.html">Migrating from Eclipse</a>.</p>
- </li>
-</ul>
-
-<p>For additional help using Android Studio, read <a
-href="{@docRoot}sdk/installing/studio-tips.html">Tips and Tricks</a>.</p>
-
-
<p>As you continue developing apps, you may need to install additional versions
of Android for the emulator and other packages such as the <a
href="{@docRoot}tools/support-library/index.html">Android Support Library</a>.
@@ -642,9 +548,13 @@
function onDownloadForRealz(link) {
if ($("input#agree").is(':checked')) {
- $("#tos").hide();
- $("#main").show();
- location.hash = "Updating";
+ $("h1").text('Now downloading Android Studio...');
+ $("#tos").slideUp();
+ $("#jd-content .jd-descr").fadeOut('slow', function() {
+ setTimeout(function() {
+ window.location = "/sdk/installing/index.html?pkg=studio";
+ }, 1000);
+ });
_gaq.push(['_trackEvent', 'SDK', 'Android Studio', $("#downloadForRealz").html()]);
return true;
} else {
diff --git a/docs/html/tools/help/sdk-manager.jd b/docs/html/tools/help/sdk-manager.jd
index 57271bb..b084237 100644
--- a/docs/html/tools/help/sdk-manager.jd
+++ b/docs/html/tools/help/sdk-manager.jd
@@ -3,7 +3,9 @@
<p>The Android SDK separates tools, platforms, and other components into packages you can
- download using the SDK Manager.</p>
+ download using the SDK Manager. For example, when the SDK Tools are updated or a new version of
+the Android platform is released, you can use the SDK Manager to quickly download them to
+your environment.</p>
<p>You can launch the SDK Manager in one of the following ways:</p>
<ul>
@@ -25,6 +27,14 @@
SDK packages that are available, already installed, or for which an update is available.</p>
+<p>There are several different packages available for the Android SDK. The table below describes
+most of the available packages and where they're located in your SDK directory
+once you download them.</p>
+
+
+
+
+
<h2 id="Recommended">Recommended Packages</h2>
<p>Here's an outline of the packages required and those we recommend you use:
@@ -69,3 +79,77 @@
<p class="note"><strong>Tip:</strong> For easy access to the SDK tools from a command line, add the
location of the SDK's <code>tools/</code> and
<code>platform-tools</code> to your <code>PATH</code> environment variable.</p>
+
+
+<p>The above list is not comprehensive and you can <a
+href="#AddingSites">add new sites</a> to download additional packages from third-parties.</p>
+
+<p>In some cases, an SDK package may require a specific minimum revision of
+another package or SDK tool.
+The development tools will notify you with warnings if there is dependency that you need to
+address. The Android SDK Manager also enforces dependencies by requiring that you download any
+packages that are needed by those you have selected.</p>
+
+
+
+
+
+<h2 id="AddingSites">Adding New Sites</h2>
+
+<p>By default, <strong>Available Packages</strong> displays packages available from the
+<em>Android Repository</em> and <em>Third party Add-ons</em>. You can add other sites that host
+their own Android SDK add-ons, then download the SDK add-ons
+from those sites.</p>
+
+<p>For example, a mobile carrier or device manufacturer might offer additional
+API libraries that are supported by their own Android-powered devices. In order
+to develop using their libraries, you must install their Android SDK add-on, if it's not already
+available under <em>Third party Add-ons</em>. </p>
+
+<p>If a carrier or device manufacturer has hosted an SDK add-on repository file
+on their web site, follow these steps to add their site to the Android SDK
+Manager:</p>
+
+<ol>
+ <li>Select <strong>Available Packages</strong> in the left panel.</li>
+ <li>Click <strong>Add Add-on Site</strong> and enter the URL of the
+<code>repository.xml</code> file. Click <strong>OK</strong>.</li>
+</ol>
+<p>Any SDK packages available from the site will now be listed under a new item named
+<strong>User Add-ons</strong>.</p>
+
+
+
+
+<h2 id="troubleshooting">Troubleshooting</h2>
+
+<p><strong>Problems connecting to the SDK repository</strong></p>
+
+<p>If you are using the Android SDK Manager to download packages and are encountering
+connection problems, try connecting over http, rather than https. To switch the
+protocol used by the Android SDK Manager, follow these steps: </p>
+
+<ol>
+ <li>With the Android SDK Manager window open, select "Settings" in the
+ left pane. </li>
+ <li>On the right, in the "Misc" section, check the checkbox labeled "Force
+ https://... sources to be fetched using http://..." </li>
+ <li>Click <strong>Save & Apply</strong>.</li>
+</ol>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd
index 9b06a9d..f490053 100644
--- a/docs/html/tools/sdk/tools-notes.jd
+++ b/docs/html/tools/sdk/tools-notes.jd
@@ -28,6 +28,39 @@
<div class="toggle-content opened">
<p><a href="#" onclick="return toggleContent(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+ alt=""/>SDK Tools, Revision 22.6.4</a> <em>(June 2014)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+
+ <dl>
+ <dt>Dependencies:</dt>
+
+ <dd>
+ <ul>
+ <li>Android SDK Platform-tools revision 18 or later.</li>
+ <li>If you are developing in Eclipse with ADT, note that this version of SDK Tools is
+ designed for use with ADT 22.6.3 and later. If you haven't already, update your
+ <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a> to 22.6.3.</li>
+ <li>If you are developing outside Eclipse, you must have
+ <a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li>
+ </ul>
+ </dd>
+
+ <dt>General Notes:</dt>
+ <dd>
+ <ul>
+ <li>Fixed an issue with the x86 emulator that caused Google Maps to crash.
+ (<a href="http://b.android.com/69385">Issue 69385</a>)</li>
+ <li>Fixed minor OpenGL issues.</li>
+ </ul>
+ </dd>
+ </div>
+</div>
+
+<div class="toggle-content closed">
+ <p><a href="#" onclick="return toggleContent(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
alt=""/>SDK Tools, Revision 22.6.3</a> <em>(April 2014)</em>
</p>
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index b29b87c..450a5c4 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -10,44 +10,32 @@
<div class="nav-section-header"><a href="<?cs var:toroot
?>sdk/index.html"><span class="en">Download</span></a></div>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/installing/bundle.html">
- <span class="en">Setting Up the ADT Bundle</span></a></li>
+ <li><a href="<?cs var:toroot ?>sdk/installing/index.html">
+ <span class="en">Installing the SDK</span></a></li>
- <li class="nav-section">
- <div class="nav-section-header">
- <a href="<?cs var:toroot ?>sdk/installing/index.html"><span class="en">Setting Up
- an Existing IDE</span></a></div>
- <ul>
- <li><a href="<?cs var:toroot ?>sdk/installing/installing-adt.html">
- <span class="en">Installing the Eclipse Plugin</span></a></li>
- <li><a href="<?cs var:toroot ?>sdk/installing/adding-packages.html">
- <span class="en">Adding Platforms and Packages</span></a></li>
- </ul>
- </li>
-
- <li class="nav-section">
- <div class="nav-section-header">
- <a href="<?cs var:toroot ?>sdk/installing/studio.html">Android Studio</a>
- </div>
- <ul>
- <li><a href="<?cs var:toroot ?>sdk/installing/migrate.html">
- Migrating from Eclipse</a></li>
- <li><a href="<?cs var:toroot ?>sdk/installing/studio-tips.html">
- Tips and Tricks</a></li>
- <li><a href="<?cs var:toroot ?>sdk/installing/studio-layout.html">
- Using the Layout Editor</a></li>
- <li><a href="<?cs var:toroot ?>sdk/installing/studio-build.html">
- Building Your Project with Gradle</a></li>
- <li><a href="<?cs var:toroot ?>sdk/installing/studio-debug.html">
- Debugging with Android Studio</a></li>
- </ul>
- </li>
- <li><a href="<?cs var:toroot ?>sdk/exploring.html">
- <span class="en">Exploring the SDK</span></a></li>
- <li><a href="<?cs var:toroot ?>tools/sdk/ndk/index.html">Download the NDK</a>
- </li>
+ <li><a href="<?cs var:toroot ?>sdk/installing/adding-packages.html">
+ <span class="en">Adding SDK Packages</span></a></li>
</ul>
</li>
+
+
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>sdk/installing/studio.html">Android Studio</a>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>sdk/installing/migrate.html">
+ Migrating from Eclipse</a></li>
+ <li><a href="<?cs var:toroot ?>sdk/installing/studio-tips.html">
+ Tips and Tricks</a></li>
+ <li><a href="<?cs var:toroot ?>sdk/installing/studio-layout.html">
+ Using the Layout Editor</a></li>
+ <li><a href="<?cs var:toroot ?>sdk/installing/studio-build.html">
+ Building Your Project with Gradle</a></li>
+ <li><a href="<?cs var:toroot ?>sdk/installing/studio-debug.html">
+ Debugging with Android Studio</a></li>
+ </ul>
+ </li>
<li class="nav-section">
<div class="nav-section-header">
@@ -174,7 +162,13 @@
class="en">Tools Help</span></a></div>
<ul>
<li><a href="<?cs var:toroot ?>tools/help/adb.html">adb</a></li>
- <li><a href="<?cs var:toroot ?>tools/help/adt.html">ADT</a></li>
+ <li class="nav-section">
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>tools/help/adt.html">ADT</a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>sdk/installing/installing-adt.html">
+ <span class="en">Installing the Eclipse Plugin</span></a></li>
+ </ul>
+ </li>
<li><a href="<?cs var:toroot ?>tools/help/android.html">android</a></li>
<li><a href="<?cs var:toroot ?>tools/help/avd-manager.html">AVD Manager</a></li>
<li><a href="<?cs var:toroot ?>tools/help/bmgr.html">bmgr</a>
@@ -244,6 +238,11 @@
</ul>
</li>
+ <li class="nav-section">
+ <div class="nav-section-header empty">
+ <a href="<?cs var:toroot ?>tools/sdk/ndk/index.html">NDK</a>
+ </div>
+ </li>
<li class="nav-section">
<div class="nav-section-header">
diff --git a/docs/html/training/articles/keystore.jd b/docs/html/training/articles/keystore.jd
new file mode 100644
index 0000000..bbbda67
--- /dev/null
+++ b/docs/html/training/articles/keystore.jd
@@ -0,0 +1,107 @@
+page.title=Android Keystore System
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#WhichShouldIUse">Choosing Between a Keychain or the Android Keystore Provider</a></li>
+ <li><a href="#UsingAndroidKeyStore">Using Android Keystore Provider
+ </a></li>
+ <ol>
+ <li><a href="#GeneratingANewPrivateKey">Generating a New Private Key</a></li>
+ <li><a href="#WorkingWithKeyStoreEntries">Working with Keystore Entries</a></li>
+ <li><a href="#ListingEntries">Listing Entries</a></li>
+ <li><a href="#SigningAndVerifyingData">Signing and Verifying Data</a></li>
+ </ol>
+ </ol>
+
+ <h2>Blog articles</h2>
+ <ol>
+ <li><a
+ href="http://android-developers.blogspot.com/2012/03/unifying-key-store-access-in-ics.html">
+ <h4>Unifying Key Store Access in ICS</h4>
+ </a></li>
+ </ol>
+ </div>
+</div>
+
+<p>The Android Keystore system lets you store private keys
+ in a container to make it more difficult to extract from the
+ device. Once keys are in the keystore, they can be used for
+ cryptographic operations with the private key material remaining
+ non-exportable.</p>
+
+<p>The Keystore system is used by the {@link
+ android.security.KeyChain} API as well as the Android
+ Keystore provider feature that was introduced in Android 4.3
+ (API level 18). This document goes over when and how to use the
+ Android Keystore provider.</p>
+
+<h2 id="WhichShouldIUse">Choosing Between a Keychain or the
+Android Keystore Provider</h2>
+
+<p>Use the {@link android.security.KeyChain} API when you want
+ system-wide credentials. When an app requests the use of any credential
+ through the {@link android.security.KeyChain} API, users get to
+ choose, through a system-provided UI, which of the installed credentials
+ an app can access. This allows several apps to use the
+ same set of credentials with user consent.</p>
+
+<p>Use the Android Keystore provider to let an individual app store its own
+ credentials that only the app itself can access.
+ This provides a way for apps to manage credentials that are usable
+ only by itself while providing the same security benefits that the
+ {@link android.security.KeyChain} API provides for system-wide
+ credentials. This method requires no user interaction to select the credentials.</p>
+
+<h2 id="UsingAndroidKeyStore">Using Android Keystore Provider</h2>
+
+<p>
+To use this feature, you use the standard {@link java.security.KeyStore}
+and {@link java.security.KeyPairGenerator} classes along with the
+{@code AndroidKeyStore} provider introduced in Android 4.3 (API level 18).</p>
+
+<p>{@code AndroidKeyStore} is registered as a {@link
+ java.security.KeyStore} type for use with the {@link
+ java.security.KeyStore#getInstance(String) KeyStore.getInstance(type)}
+ method and as a provider for use with the {@link
+ java.security.KeyPairGenerator#getInstance(String, String)
+ KeyPairGenerator.getInstance(algorithm, provider)} method.</p>
+
+<h3 id="GeneratingANewPrivateKey">Generating a New Private Key</h3>
+
+<p>Generating a new {@link java.security.PrivateKey} requires that
+ you also specify the initial X.509 attributes that the self-signed
+ certificate will have. You can replace the certificate at a later
+ time with a certificate signed by a Certificate Authority.</p>
+
+<p>To generate the key, use a {@link java.security.KeyPairGenerator}
+ with {@link android.security.KeyPairGeneratorSpec}:</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java generate}
+
+<h3 id="WorkingWithKeyStoreEntries">Working with Keystore Entries</h3>
+
+<p>Using the {@code AndroidKeyStore} provider takes place through
+ all the standard {@link java.security.KeyStore} APIs.</p>
+
+<h4 id="ListingEntries">Listing Entries</h4>
+
+<p>List entries in the keystore by calling the {@link
+ java.security.KeyStore#aliases()} method:</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java list}
+
+<h4 id="SigningAndVerifyingData">Signing and Verifying Data</h4>
+
+<p>Sign data by fetching the {@link
+ java.security.KeyStore.Entry} from the keystore and using the
+ {@link java.security.Signature} APIs, such as {@link
+ java.security.Signature#sign()}:</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java sign}
+
+<p>Similarly, verify data with the {@link java.security.Signature#verify(byte[])} method:</p>
+
+{@sample development/samples/ApiDemos/src/com/example/android/apis/security/KeyStoreUsage.java verify}
diff --git a/docs/html/training/basics/network-ops/connecting.jd b/docs/html/training/basics/network-ops/connecting.jd
index 50a9e1b..1452ded 100644
--- a/docs/html/training/basics/network-ops/connecting.jd
+++ b/docs/html/training/basics/network-ops/connecting.jd
@@ -25,6 +25,7 @@
<h2>You should also read</h2>
<ul>
+ <li><a href="{@docRoot}training/volley/index.html">Transmitting Network Data Using Volley</a></li>
<li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
<li><a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a></li>
<li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li>
diff --git a/docs/html/training/basics/network-ops/index.jd b/docs/html/training/basics/network-ops/index.jd
index 89ab539..1f6493f 100644
--- a/docs/html/training/basics/network-ops/index.jd
+++ b/docs/html/training/basics/network-ops/index.jd
@@ -24,6 +24,7 @@
<li><a href="{@docRoot}training/monitoring-device-state/index.html">Optimizing Battery Life</a></li>
<li><a href="{@docRoot}training/efficient-downloads/index.html">Transferring Data Without Draining the Battery</a></li>
<li><a href="{@docRoot}guide/webapps/index.html">Web Apps Overview</a></li>
+ <li><a href="{@docRoot}training/volley/index.html">Transmitting Network Data Using Volley</a></li>
</ul>
@@ -51,6 +52,14 @@
fundamental building blocks for creating Android applications that download
content and parse data efficiently, while minimizing network traffic.</p>
+<p class="note"><strong>Note:</strong> See the class <a href="{@docRoot}
+training/volley/index.html">Transmitting Network Data Using Volley</a>
+for information on Volley, an HTTP library that makes networking for Android apps
+easier and faster. Volley is available through the open
+<a href="https://android.googlesource.com/platform/frameworks/volley">AOSP</a>
+repository. Volley may be able to help you streamline and improve the performance
+of your app's network operations.</p>
+
<h2>Lessons</h2>
diff --git a/docs/html/training/contacts-provider/retrieve-names.jd b/docs/html/training/contacts-provider/retrieve-names.jd
index b034a6a..7106889a 100644
--- a/docs/html/training/contacts-provider/retrieve-names.jd
+++ b/docs/html/training/contacts-provider/retrieve-names.jd
@@ -102,9 +102,9 @@
<p>
To display the search results in a {@link android.widget.ListView}, you need a main layout file
that defines the entire UI including the {@link android.widget.ListView}, and an item layout
- file that defines one line of the {@link android.widget.ListView}. For example, you can define
- the main layout file <code>res/layout/contacts_list_view.xml</code> that contains the
- following XML:
+ file that defines one line of the {@link android.widget.ListView}. For example, you could create
+ the main layout file <code>res/layout/contacts_list_view.xml</code> with
+ the following XML:
</p>
<pre>
<?xml version="1.0" encoding="utf-8"?>
@@ -250,7 +250,8 @@
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the fragment layout
- return inflater.inflate(R.layout.contacts_list_layout, container, false);
+ return inflater.inflate(R.layout.contact_list_fragment,
+ container, false);
}
</pre>
<h3 id="DefineAdapter">Set up the CursorAdapter for the ListView</h3>
@@ -268,7 +269,8 @@
super.onActivityCreated(savedInstanceState);
...
// Gets the ListView from the View list of the parent activity
- mContactsList = (ListView) getActivity().findViewById(R.layout.contact_list_view);
+ mContactsList =
+ (ListView) getActivity().findViewById(R.layout.contact_list_view);
// Gets a CursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
getActivity(),
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 0616b62..c5dc3c5 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -609,6 +609,35 @@
</li>
</ul>
</li>
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>training/volley/index.html"
+ description="How to perform fast, scalable UI operations over the network using Volley"
+ >Transmitting Network Data Using Volley</a>
+ </div>
+ <ul>
+ <li>
+ <a href="<?cs var:toroot ?>training/volley/simple.html">
+ Sending a Simple Request
+ </a>
+ </li>
+ <li>
+ <a href="<?cs var:toroot ?>training/volley/requestqueue.html">
+ Setting Up a RequestQueue
+ </a>
+ </li>
+ <li>
+ <a href="<?cs var:toroot ?>training/volley/request.html">
+ Making a Standard Request
+ </a>
+ </li>
+ <li>
+ <a href="<?cs var:toroot ?>training/volley/request-custom.html">
+ Implementing a Custom Request
+ </a>
+ </li>
+ </ul>
+ </li>
</ul>
</li>
diff --git a/docs/html/training/volley/index.jd b/docs/html/training/volley/index.jd
new file mode 100644
index 0000000..ba5b09f
--- /dev/null
+++ b/docs/html/training/volley/index.jd
@@ -0,0 +1,133 @@
+page.title=Transmitting Network Data Using Volley
+page.tags=""
+
+trainingnavtop=true
+startpage=true
+
+
+@jd:body
+
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+
+<ul>
+ <li>Android 1.6 (API Level 4) or higher</li>
+</ul>
+
+<h2>You should also see</h2>
+<ul>
+ <li>For a production quality app that uses Volley, see the 2013 Google I/O
+ <a href="https://github.com/google/iosched">schedule app</a>. In particular, see:
+ <ul>
+ <li><a
+ href="https://github.com/google/iosched/blob/master/android/src/main/java/com/google/android/apps/iosched/util/ImageLoader.java">
+ ImageLoader</a></li>
+ <li><a
+ href="https://github.com/google/iosched/blob/master/android/src/main/java/com/google/android/apps/iosched/util/BitmapCache.java">
+ BitmapCache</a></li>
+ </ul>
+ </li>
+</ul>
+
+</div>
+</div>
+
+<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728">
+<div>
+ <h3>Video</h3>
+ <p>Volley: Easy, Fast Networking for Android</p>
+</div>
+</a>
+
+
+<p>Volley is an HTTP library that makes networking for Android apps easier and most importantly,
+faster. Volley is available through the open
+<a href="https://android.googlesource.com/platform/frameworks/volley">AOSP</a> repository.</p>
+
+<p>Volley offers the following benefits:</p>
+
+<ul>
+
+<li>Automatic scheduling of network requests.</li>
+<li>Multiple concurrent network connections.</li>
+<li>Transparent disk and memory response caching with standard HTTP
+<a href=http://en.wikipedia.org/wiki/Cache_coherence">cache coherence</a>.</li>
+<li>Support for request prioritization.</li>
+<li>Cancellation request API. You can cancel a single request, or you can set blocks or
+scopes of requests to cancel.</li>
+<li>Ease of customization, for example, for retry and backoff.</li>
+<li>Strong ordering that makes it easy to correctly populate your UI with data fetched
+asynchronously from the network.</li>
+<li>Debugging and tracing tools.</li>
+
+</ul>
+
+<p>Volley excels at RPC-type operations used to populate a UI, such as fetching a page of
+search results as structured data. It integrates easily with any protocol and comes out of
+the box with support for raw strings, images, and JSON. By providing built-in support for
+the features you need, Volley frees you from writing boilerplate code and allows you to
+concentrate on the logic that is specific to your app.</p>
+<p>Volley is not suitable for large download or streaming operations, since Volley holds
+all responses in memory during parsing. For large download operations, consider using an
+alternative like {@link android.app.DownloadManager}.</p>
+
+<p>The core Volley library is developed in the open
+<a href="https://android.googlesource.com/platform/frameworks/volley">AOSP</a>
+repository at {@code frameworks/volley} and contains the main request dispatch pipeline
+as well as a set of commonly applicable utilities, available in the Volley "toolbox." The
+easiest way to add Volley to your project is to clone the Volley repository and set it as
+a library project:</p>
+
+<ol>
+<li>Git clone the repository by typing the following at the command line:
+
+<pre>
+git clone https://android.googlesource.com/platform/frameworks/volley
+</pre>
+</li>
+
+<li>Import the downloaded source into your app project as an Android library project
+(as described in <a href="{@docRoot}tools/projects/projects-eclipse.html">
+Managing Projects from Eclipse with ADT</a>, if you're using Eclipse) or make a
+<a href="{@docRoot}guide/faq/commontasks.html#addexternallibrary"><code>.jar</code> file</a>.</li>
+</ol>
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt>
+ <strong><a href="simple.html">Sending a Simple Request</a></strong>
+ </dt>
+ <dd>
+ Learn how to send a simple request using the default behaviors of Volley, and how
+ to cancel a request.
+
+ </dd>
+ <dt>
+ <strong><a href="requestqueue.html">Setting Up a RequestQueue</a></strong>
+ </dt>
+ <dd>
+ Learn how to set up a {@code RequestQueue}, and how to implement a singleton
+ pattern to create a {@code RequestQueue} that lasts the lifetime of your app.
+ </dd>
+ <dt>
+ <strong><a href="request.html">Making a Standard Request</a></strong>
+ </dt>
+ <dd>
+ Learn how to send a request using one of Volley's out-of-the-box request types
+ (raw strings, images, and JSON).
+ </dd>
+ <dt>
+ <strong><a href="request-custom.html">Implementing a Custom Request</a></strong>
+ </dt>
+ <dd>
+ Learn how to implement a custom request.
+ </dd>
+
+</dl>
diff --git a/docs/html/training/volley/request-custom.jd b/docs/html/training/volley/request-custom.jd
new file mode 100644
index 0000000..7b669b9
--- /dev/null
+++ b/docs/html/training/volley/request-custom.jd
@@ -0,0 +1,163 @@
+page.title=Implementing a Custom Request
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#custom-request">Write a Custom Request</a></li>
+</ol>
+
+</div>
+</div>
+
+<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728">
+<div>
+ <h3>Video</h3>
+ <p>Volley: Easy, Fast Networking for Android</p>
+</div>
+</a>
+
+<p>This lesson describes how to implement your own custom request types, for types that
+don't have out-of-the-box Volley support.</p>
+
+<h2 id="custom-request">Write a Custom Request</h2>
+
+Most requests have ready-to-use implementations in the toolbox; if your response is a string,
+image, or JSON, you probably won't need to implement a custom {@code Request}.</p>
+
+<p>For cases where you do need to implement a custom request, this is all you need
+to do:</p>
+
+<ul>
+
+<li>Extend the {@code Request<T>} class, where
+{@code <T>} represents the type of parsed response
+the request expects. So if your parsed response is a string, for example,
+create your custom request by extending {@code Request<String>}. See the Volley
+toolbox classes {@code StringRequest} and {@code ImageRequest} for examples of
+extending {@code Request<T>}.</li>
+
+<li>Implement the abstract methods {@code parseNetworkResponse()}
+and {@code deliverResponse()}, described in more detail below.</li>
+
+</ul>
+
+<h3>parseNetworkResponse</h3>
+
+<p>A {@code Response} encapsulates a parsed response for delivery, for a given type
+(such as string, image, or JSON). Here is a sample implementation of
+{@code parseNetworkResponse()}:</p>
+
+<pre>
+@Override
+protected Response<T> parseNetworkResponse(
+ NetworkResponse response) {
+ try {
+ String json = new String(response.data,
+ HttpHeaderParser.parseCharset(response.headers));
+ return Response.success(gson.fromJson(json, clazz),
+ HttpHeaderParser.parseCacheHeaders(response));
+ }
+ // handle errors
+...
+}
+</pre>
+
+<p>Note the following:</p>
+
+<ul>
+<li>{@code parseNetworkResponse()} takes as its parameter a {@code NetworkResponse}, which
+contains the response payload as a byte[], HTTP status code, and response headers.</li>
+<li>Your implementation must return a {@code Response<T>}, which contains your typed
+response object and cache metadata or an error, such as in the case of a parse failure.</li>
+</ul>
+
+<p>If your protocol has non-standard cache semantics, you can build a {@code Cache.Entry}
+yourself, but most requests are fine with something like this:
+</p>
+<pre>return Response.success(myDecodedObject,
+ HttpHeaderParser.parseCacheHeaders(response));</pre>
+<p>
+Volley calls {@code parseNetworkResponse()} from a worker thread. This ensures that
+expensive parsing operations, such as decoding a JPEG into a Bitmap, don't block the UI
+thread.</p>
+
+<h3>deliverResponse</h3>
+
+<p>Volley calls you back on the main thread with the object you returned in
+{@code parseNetworkResponse()}. Most requests invoke a callback interface here,
+for example:
+</p>
+
+<pre>
+protected void deliverResponse(T response) {
+ listener.onResponse(response);
+</pre>
+
+<h3>Example: GsonRequest</h3>
+
+<p><a href="http://code.google.com/p/google-gson/">Gson</a> is a library for converting
+Java objects to and from JSON using reflection. You can define Java objects that have the
+same names as their corresponding JSON keys, pass Gson the class object, and Gson will fill
+in the fields for you. Here's a complete implementation of a Volley request that uses
+Gson for parsing:</p>
+
+<pre>
+public class GsonRequest<T> extends Request<T> {
+ private final Gson gson = new Gson();
+ private final Class<T> clazz;
+ private final Map<String, String> headers;
+ private final Listener<T> listener;
+
+ /**
+ * Make a GET request and return a parsed object from JSON.
+ *
+ * @param url URL of the request to make
+ * @param clazz Relevant class object, for Gson's reflection
+ * @param headers Map of request headers
+ */
+ public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
+ Listener<T> listener, ErrorListener errorListener) {
+ super(Method.GET, url, errorListener);
+ this.clazz = clazz;
+ this.headers = headers;
+ this.listener = listener;
+ }
+
+ @Override
+ public Map<String, String> getHeaders() throws AuthFailureError {
+ return headers != null ? headers : super.getHeaders();
+ }
+
+ @Override
+ protected void deliverResponse(T response) {
+ listener.onResponse(response);
+ }
+
+ @Override
+ protected Response<T> parseNetworkResponse(NetworkResponse response) {
+ try {
+ String json = new String(
+ response.data,
+ HttpHeaderParser.parseCharset(response.headers));
+ return Response.success(
+ gson.fromJson(json, clazz),
+ HttpHeaderParser.parseCacheHeaders(response));
+ } catch (UnsupportedEncodingException e) {
+ return Response.error(new ParseError(e));
+ } catch (JsonSyntaxException e) {
+ return Response.error(new ParseError(e));
+ }
+ }
+}
+</pre>
+
+<p>Volley provides ready-to-use {@code JsonArrayRequest} and {@code JsonArrayObject} classes
+if you prefer to take that approach. See <a href="request.html">
+Using Standard Request Types</a> for more information.</p>
diff --git a/docs/html/training/volley/request.jd b/docs/html/training/volley/request.jd
new file mode 100644
index 0000000..d8ccab2
--- /dev/null
+++ b/docs/html/training/volley/request.jd
@@ -0,0 +1,281 @@
+page.title=Making a Standard Request
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#request-image">Request an Image</a></li>
+ <li><a href="#request-json">Request JSON</a></li>
+</ol>
+
+</div>
+</div>
+
+<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728">
+<div>
+ <h3>Video</h3>
+ <p>Volley: Easy, Fast Networking for Android</p>
+</div>
+</a>
+
+<p>
+This lesson describes how to use the common request types that Volley supports:</p>
+
+<ul>
+ <li>{@code StringRequest}. Specify a URL and receive a raw string in response. See
+ <a href="requestqueue.html">Setting Up a Request Queue</a> for an example.</li>
+ <li>{@code ImageRequest}. Specify a URL and receive an image in response.</li>
+ <li>{@code JsonObjectRequest} and {@code JsonArrayRequest} (both subclasses of
+ {@code JsonRequest}). Specify a URL and get a JSON object or array (respectively) in
+ response.</li>
+</ul>
+
+<p>If your expected response is one of these types, you probably won't have to implement a
+custom request. This lesson describes how to use these standard request types. For
+information on how to implement your own custom request, see <a href="requests-custom.html">
+Implementing a Custom Request</a>.</p>
+
+
+<h2 id="request-image">Request an Image</h2>
+
+<p>Volley offers the following classes for requesting images. These classes layer on top
+of each other to offer different levels of support for processing images:</p>
+
+<ul>
+ <li>{@code ImageRequest}—a canned request for getting an image at a given URL and
+ calling back with a decoded bitmap. It also provides convenience features like specifying
+ a size to resize to. Its main benefit is that Volley's thread scheduling ensures that
+ expensive image operations (decoding, resizing) automatically happen on a worker thread.</li>
+
+ <li>{@code ImageLoader}—a helper class that handles loading and caching images from
+ remote URLs. {@code ImageLoader} is a an orchestrator for large numbers of {@code ImageRequest}s,
+ for example when putting multiple thumbnails in a {@link android.widget.ListView}.
+ {@code ImageLoader} provides an in-memory cache to sit in front of the normal Volley
+ cache, which is important to prevent flickering. This makes it possible to achieve a
+ cache hit without blocking or deferring off the main thread, which is impossible when
+ using disk I/O. {@code ImageLoader} also does response coalescing, without which almost
+ every response handler would set a bitmap on a view and cause a layout pass per image.
+ Coalescing makes it possible to deliver multiple responses simultaneously, which improves
+ performance.</li>
+ <li>{@code NetworkImageView}—builds on {@code ImageLoader} and effectively replaces
+ {@link android.widget.ImageView} for situations where your image is being fetched over
+ the network via URL. {@code NetworkImageView} also manages canceling pending requests if
+ the view is detached from the hierarchy.</li>
+</ul>
+
+<h3>Use ImageRequest</h3>
+
+<p>Here is an example of using {@code ImageRequest}. It retrieves the image specified by
+the URL and displays it in the app. Note that this snippet interacts with the
+{@code RequestQueue} through a singleton class (see <a href="{@docRoot}
+training/volley/requestqueue.html#singleton">Setting Up a RequestQueue</a> for more discussion of
+this topic):</p>
+
+<pre>
+ImageView mImageView;
+String url = "http://i.imgur.com/7spzG.png";
+mImageView = (ImageView) findViewById(R.id.myImage);
+...
+
+// Retrieves an image specified by the URL, displays it in the UI.
+ImageRequest request = new ImageRequest(url,
+ new Response.Listener<Bitmap>() {
+ @Override
+ public void onResponse(Bitmap bitmap) {
+ mImageView.setImageBitmap(bitmap);
+ }
+ }, 0, 0, null,
+ new Response.ErrorListener() {
+ public void onErrorResponse(VolleyError error) {
+ mImageView.setImageResource(R.drawable.image_load_error);
+ }
+ });
+// Access the RequestQueue through your singleton class.
+MySingleton.getInstance(this).addToRequestQueue(request);</pre>
+
+
+<h3>Use ImageLoader and NetworkImageView</h3>
+
+<p>You can use {@code ImageLoader} and {@code NetworkImageView} in concert to efficiently
+manage the display of multiple images, such as in a {@link android.widget.ListView}. In your
+layout XML file, you use {@code NetworkImageView} in much the same way you would use
+{@link android.widget.ImageView}, for example:</p>
+
+<pre><com.android.volley.toolbox.NetworkImageView
+ android:id="@+id/networkImageView"
+ android:layout_width="150dp"
+ android:layout_height="170dp"
+ android:layout_centerHorizontal="true" /></pre>
+
+<p>You can use {@code ImageLoader} by itself to display an image, for example:</p>
+
+<pre>
+ImageLoader mImageLoader;
+ImageView mImageView;
+// The URL for the image that is being loaded.
+private static final String IMAGE_URL =
+ "http://developer.android.com/images/training/system-ui.png";
+...
+mImageView = (ImageView) findViewById(R.id.regularImageView);
+
+// Get the ImageLoader through your singleton class.
+mImageLoader = MySingleton.getInstance(this).getImageLoader();
+mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,
+ R.drawable.def_image, R.drawable.err_image));
+</pre>
+
+<p>However, {@code NetworkImageView} can do this for you if all you're doing is populating
+an {@link android.widget.ImageView}. For example:</p>
+
+<pre>
+ImageLoader mImageLoader;
+NetworkImageView mNetworkImageView;
+private static final String IMAGE_URL =
+ "http://developer.android.com/images/training/system-ui.png";
+...
+
+// Get the NetworkImageView that will display the image.
+mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);
+
+// Get the ImageLoader through your singleton class.
+mImageLoader = MySingleton.getInstance(this).getImageLoader();
+
+// Set the URL of the image that should be loaded into this view, and
+// specify the ImageLoader that will be used to make the request.
+mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);
+</pre>
+
+<p>The above snippets access the {@code RequestQueue} and the {@code ImageLoader}
+through a singleton class, as described in <a href="{@docRoot}training/volley/requestqueue.html#singleton">
+Setting Up a RequestQueue</a>. This approach ensures that your app creates single instances of
+these classes that last the lifetime of your app. The reason that this is important for
+{@code ImageLoader} (the helper class that handles loading and caching images) is that
+the main function of the in-memory cache is to allow for flickerless rotation. Using a
+singleton pattern allows the bitmap cache to outlive the activity. If instead you create the
+{@code ImageLoader} in an activity, the {@code ImageLoader} would be recreated along with
+the activity every time the user rotates the device. This would cause flickering.</p>
+
+<h4 id="lru-cache">Example LRU cache</h4>
+
+<p>The Volley toolbox provides a standard cache implementation via the
+{@code DiskBasedCache} class. This class caches files directly onto the hard disk in the
+specified directory. But to use {@code ImageLoader}, you should provide a custom
+in-memory LRU bitmap cache that implements the {@code ImageLoader.ImageCache} interface.
+You may want to set up your cache as a singleton; for more discussion of this topic, see
+<a href="{@docRoot}training/volley/requestqueue.html#singleton">
+Setting Up a RequestQueue</a>.</p>
+
+<p>Here is a sample implementation for an in-memory {@code LruBitmapCache} class.
+It extends the {@link android.support.v4.util.LruCache} class and implements the
+{@code ImageLoader.ImageCache} interface:</p>
+
+<pre>
+import android.graphics.Bitmap;
+import android.support.v4.util.LruCache;
+import android.util.DisplayMetrics;
+import com.android.volley.toolbox.ImageLoader.ImageCache;
+
+public class LruBitmapCache extends LruCache<String, Bitmap>
+ implements ImageCache {
+
+ public LruBitmapCache(int maxSize) {
+ super(maxSize);
+ }
+
+ public LruBitmapCache(Context ctx) {
+ this(getCacheSize(ctx));
+ }
+
+ @Override
+ protected int sizeOf(String key, Bitmap value) {
+ return value.getRowBytes() * value.getHeight();
+ }
+
+ @Override
+ public Bitmap getBitmap(String url) {
+ return get(url);
+ }
+
+ @Override
+ public void putBitmap(String url, Bitmap bitmap) {
+ put(url, bitmap);
+ }
+
+ // Returns a cache size equal to approximately three screens worth of images.
+ public static int getCacheSize(Context ctx) {
+ final DisplayMetrics displayMetrics = ctx.getResources().
+ getDisplayMetrics();
+ final int screenWidth = displayMetrics.widthPixels;
+ final int screenHeight = displayMetrics.heightPixels;
+ // 4 bytes per pixel
+ final int screenBytes = screenWidth * screenHeight * 4;
+
+ return screenBytes * 3;
+ }
+}
+</pre>
+
+<p>Here is an example of how to instantiate an {@code ImageLoader} to use this
+cache:</p>
+
+<pre>
+RequestQueue mRequestQueue; // assume this exists.
+ImageLoader mImageLoader = new ImageLoader(mRequestQueue, new LruBitmapCache(
+ LruBitmapCache.getCacheSize()));
+</pre>
+
+
+<h2 id="request-json">Request JSON</h2>
+
+<p>Volley provides the following classes for JSON requests:</p>
+
+<ul>
+ <li>{@code JsonArrayRequest}—A request for retrieving a
+ {@link org.json.JSONArray}
+ response body at a given URL.</li>
+ <li>{@code JsonObjectRequest}—A request for retrieving a
+ {@link org.json.JSONObject}
+ response body at a given URL, allowing for an optional
+ {@link org.json.JSONObject}
+ to be passed in as part of the request body.</li>
+</ul>
+
+<p>Both classes are based on the common base class {@code JsonRequest}. You use them
+following the same basic pattern you use for other types of requests. For example, this
+snippet fetches a JSON feed and displays it as text in the UI:</p>
+
+<pre>
+TextView mTxtDisplay;
+ImageView mImageView;
+mTxtDisplay = (TextView) findViewById(R.id.txtDisplay);
+String url = "http://my-json-feed";
+
+JsonObjectRequest jsObjRequest = new JsonObjectRequest
+ (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
+
+ @Override
+ public void onResponse(JSONObject response) {
+ mTxtDisplay.setText("Response: " + response.toString());
+ }
+}, new Response.ErrorListener() {
+
+ @Override
+ public void onErrorResponse(VolleyError error) {
+ // TODO Auto-generated method stub
+
+ }
+});
+
+// Access the RequestQueue through your singleton class.
+MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);
+</pre>
+
+For an example of implementing a custom JSON request based on
+<a href="http://code.google.com/p/google-gson/">Gson</a>, see the next lesson,
+<a href="request-custom.html">Implementing a Custom Request</a>.
diff --git a/docs/html/training/volley/requestqueue.jd b/docs/html/training/volley/requestqueue.jd
new file mode 100644
index 0000000..6858d91
--- /dev/null
+++ b/docs/html/training/volley/requestqueue.jd
@@ -0,0 +1,204 @@
+page.title=Setting Up a RequestQueue
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#network">Set Up a Network and Cache</a></li>
+ <li><a href="#singleton">Use a Singleton Pattern</a></li>
+</ol>
+
+</div>
+</div>
+
+<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728">
+<div>
+ <h3>Video</h3>
+ <p>Volley: Easy, Fast Networking for Android</p>
+</div>
+</a>
+
+
+<p>The previous lesson showed you how to use the convenience method
+<code>Volley.newRequestQueue</code> to set up a {@code RequestQueue}, taking advantage of
+Volley's default behaviors. This lesson walks you through the explicit steps of creating a
+{@code RequestQueue}, to allow you to supply your own custom behavior.</p>
+
+<p>This lesson also describes the recommended practice of creating a {@code RequestQueue}
+as a singleton, which makes the {@code RequestQueue} last the lifetime of your app.</p>
+
+<h2 id="network">Set Up a Network and Cache</h2>
+
+<p>A {@code RequestQueue} needs two things to do its job: a network to perform transport
+of the requests, and a cache to handle caching. There are standard implementations of these
+available in the Volley toolbox: {@code DiskBasedCache} provides a one-file-per-response
+cache with an in-memory index, and {@code BasicNetwork} provides a network transport based
+on your choice of {@link android.net.http.AndroidHttpClient} or {@link java.net.HttpURLConnection}.</p>
+
+<p>{@code BasicNetwork} is Volley's default network implementation. A {@code BasicNetwork}
+must be initialized with the HTTP client your app is using to connect to the network.
+Typically this is {@link android.net.http.AndroidHttpClient} or
+{@link java.net.HttpURLConnection}:</p>
+<ul>
+<li>Use {@link android.net.http.AndroidHttpClient} for apps targeting Android API levels
+lower than API Level 9 (Gingerbread). Prior to Gingerbread, {@link java.net.HttpURLConnection}
+was unreliable. For more discussion of this topic, see
+<a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">
+Android's HTTP Clients</a>. </li>
+
+<li>Use {@link java.net.HttpURLConnection} for apps targeting Android API Level 9
+(Gingerbread) and higher.</li>
+</ul>
+<p>To create an app that runs on all versions of Android, you can check the version of
+Android the device is running and choose the appropriate HTTP client, for example:</p>
+
+<pre>
+HttpStack stack;
+...
+// If the device is running a version >= Gingerbread...
+if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
+ // ...use HttpURLConnection for stack.
+} else {
+ // ...use AndroidHttpClient for stack.
+}
+Network network = new BasicNetwork(stack);
+</pre>
+
+<p>This snippet shows you the steps involved in setting up a
+{@code RequestQueue}:</p>
+
+<pre>
+RequestQueue mRequestQueue;
+
+// Instantiate the cache
+Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap
+
+// Set up the network to use HttpURLConnection as the HTTP client.
+Network network = new BasicNetwork(new HurlStack());
+
+// Instantiate the RequestQueue with the cache and network.
+mRequestQueue = new RequestQueue(cache, network);
+
+// Start the queue
+mRequestQueue.start();
+
+String url ="http://www.myurl.com";
+
+// Formulate the request and handle the response.
+StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
+ new Response.Listener<String>() {
+ @Override
+ public void onResponse(String response) {
+ // Do something with the response
+ }
+},
+ new Response.ErrorListener() {
+ @Override
+ public void onErrorResponse(VolleyError error) {
+ // Handle error
+ }
+});
+
+// Add the request to the RequestQueue.
+mRequestQueue.add(stringRequest);
+...
+</pre>
+
+<p>If you just need to make a one-time request and don't want to leave the thread pool
+around, you can create the {@code RequestQueue} wherever you need it and call {@code stop()} on the
+{@code RequestQueue} once your response or error has come back, using the
+{@code Volley.newRequestQueue()} method described in <a href="simple.html">Sending a Simple
+Request</a>. But the more common use case is to create the {@code RequestQueue} as a
+singleton to keep it running for the lifetime of your app, as described in the next section.</p>
+
+
+<h2 id="singleton">Use a Singleton Pattern</h2>
+
+<p>If your application makes constant use of the network, it's probably most efficient to
+set up a single instance of {@code RequestQueue} that will last the lifetime of your app.
+You can achieve this in various ways. The recommended approach is to implement a singleton
+class that encapsulates {@code RequestQueue} and other Volley
+functionality. Another approach is to subclass {@link android.app.Application} and set up the
+{@code RequestQueue} in {@link android.app.Application#onCreate Application.onCreate()}.
+But this approach is <a href="{@docRoot}reference/android/app/Application.html">
+discouraged</a>; a static singleton can provide the same functionality in a more modular
+way. </p>
+
+<p>A key concept is that the {@code RequestQueue} must be instantiated with the
+{@link android.app.Application} context, not an {@link android.app.Activity} context. This
+ensures that the {@code RequestQueue} will last for the lifetime of your app, instead of
+being recreated every time the activity is recreated (for example, when the user
+rotates the device).
+
+<p>Here is an example of a singleton class that provides {@code RequestQueue} and
+{@code ImageLoader} functionality:</p>
+
+<pre>private static MySingleton mInstance;
+ private RequestQueue mRequestQueue;
+ private ImageLoader mImageLoader;
+ private static Context mCtx;
+
+ private MySingleton(Context context) {
+ mCtx = context;
+ mRequestQueue = getRequestQueue();
+
+ mImageLoader = new ImageLoader(mRequestQueue,
+ new ImageLoader.ImageCache() {
+ private final LruCache<String, Bitmap>
+ cache = new LruCache<String, Bitmap>(20);
+
+ @Override
+ public Bitmap getBitmap(String url) {
+ return cache.get(url);
+ }
+
+ @Override
+ public void putBitmap(String url, Bitmap bitmap) {
+ cache.put(url, bitmap);
+ }
+ });
+ }
+
+ public static synchronized MySingleton getInstance(Context context) {
+ if (mInstance == null) {
+ mInstance = new MySingleton(context);
+ }
+ return mInstance;
+ }
+
+ public RequestQueue getRequestQueue() {
+ if (mRequestQueue == null) {
+ // getApplicationContext() is key, it keeps you from leaking the
+ // Activity or BroadcastReceiver if someone passes one in.
+ mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
+ }
+ return mRequestQueue;
+ }
+
+ public <T> void addToRequestQueue(Request<T> req) {
+ getRequestQueue().add(req);
+ }
+
+ public ImageLoader getImageLoader() {
+ return mImageLoader;
+ }
+}</pre>
+
+<p>Here are some examples of performing {@code RequestQueue} operations using the singleton
+class:</p>
+
+<pre>
+// Get a RequestQueue
+RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
+ getRequestQueue();
+...
+
+// Add a request (in this example, called stringRequest) to your RequestQueue.
+MySingleton.getInstance(this).addToRequestQueue(stringRequest);
+</pre>
diff --git a/docs/html/training/volley/simple.jd b/docs/html/training/volley/simple.jd
new file mode 100644
index 0000000..942c57f
--- /dev/null
+++ b/docs/html/training/volley/simple.jd
@@ -0,0 +1,169 @@
+page.title=Sending a Simple Request
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#manifest">Add the INTERNET Permission</a></li>
+ <li><a href="#simple">Use newRequestQueue</a></li>
+ <li><a href="#send">Send a Request</a></li>
+ <li><a href="#cancel">Cancel a Request</a></li>
+</ol>
+
+</div>
+</div>
+
+<a class="notice-developers-video wide" href="https://developers.google.com/events/io/sessions/325304728">
+<div>
+ <h3>Video</h3>
+ <p>Volley: Easy, Fast Networking for Android</p>
+</div>
+</a>
+
+<p>At a high level, you use Volley by creating a {@code RequestQueue} and passing it
+{@code Request} objects. The {@code RequestQueue} manages worker threads for running the
+network operations, reading from and writing to the cache, and parsing responses. Requests
+do the parsing of raw responses and Volley takes care of dispatching the parsed response
+back to the main thread for delivery.</p>
+
+<p> This lesson describes how to send a request using the <code>Volley.newRequestQueue</code>
+convenience method, which sets up a {@code RequestQueue} for you.
+See the next lesson,
+<a href="requestqueue.html">Setting Up a RequestQueue</a>, for information on how to set
+up a {@code RequestQueue} yourself.</p>
+
+<p>This lesson also describes how to add a request to a {@code RequestQueue} and cancel a
+request.</p>
+
+<h2 id="manifest">Add the INTERNET Permission</h2>
+
+<p>To use Volley, you must add the
+{@link android.Manifest.permission#INTERNET android.permission.INTERNET} permission
+to your app's manifest. Without this, your app won't be able to connect to the network.</p>
+
+
+<h2 id="simple">Use newRequestQueue</h2>
+
+<p>Volley provides a convenience method <code>Volley.newRequestQueue</code> that sets up a
+{@code RequestQueue} for you, using default values, and starts the queue. For example:</p>
+
+<pre>
+final TextView mTextView = (TextView) findViewById(R.id.text);
+...
+
+// Instantiate the RequestQueue.
+RequestQueue queue = Volley.newRequestQueue(this);
+String url ="http://www.google.com";
+
+// Request a string response from the provided URL.
+StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
+ new Response.Listener<String>() {
+ @Override
+ public void onResponse(String response) {
+ // Display the first 500 characters of the response string.
+ mTextView.setText("Response is: "+ response.substring(0,500));
+ }
+}, new Response.ErrorListener() {
+ @Override
+ public void onErrorResponse(VolleyError error) {
+ mTextView.setText("That didn't work!");
+ }
+});
+// Add the request to the RequestQueue.
+queue.add(stringRequest);
+</pre>
+
+<p>Volley always delivers parsed responses on the main thread. Running on the main thread
+is convenient for populating UI controls with received data, as you can freely modify UI
+controls directly from your response handler, but it's especially critical to many of the
+important semantics provided by the library, particularly related to canceling requests.
+</p>
+
+<p>See <a href="requestqueue.html">Setting Up a RequestQueue</a> for a
+description of how to set up a {@code RequestQueue} yourself, instead of using the
+<code>Volley.newRequestQueue</code> convenience method.</p>
+
+<h2 id="send">Send a Request</h2>
+
+<p>To send a request, you simply construct one and add it to the {@code RequestQueue} with
+{@code add()}, as shown above. Once you add the request it moves through the pipeline,
+gets serviced, and has its raw response parsed and delivered.</p>
+
+<p>When you call {@code add()}, Volley runs one cache processing thread and a pool of
+network dispatch threads. When you add a request to the queue, it is picked up by the cache
+thread and triaged: if the request can be serviced from cache, the cached response is
+parsed on the cache thread and the parsed response is delivered on the main thread. If the
+request cannot be serviced from cache, it is placed on the network queue. The first
+available network thread takes the request from the queue, performs the HTTP transaction,
+parsse the response on the worker thread, writes the response to cache, and posts the parsed
+response back to the main thread for delivery.</p>
+
+<p>Note that expensive operations like blocking I/O and parsing/decoding are done on worker
+threads. You can add a request from any thread, but responses are always delivered on the
+main thread.</p>
+
+<p>Figure 1 illustrates the life of a request:</p>
+
+ <img src="{@docRoot}images/training/volley-request.png"
+ alt="system bars">
+<p class="img-caption"><strong>Figure 1.</strong> Life of a request.</p>
+
+
+<h2 id="cancel">Cancel a Request</h2>
+
+<p>To cancel a request, call {@code cancel()} on your {@code Request} object. Once cancelled,
+Volley guarantees that your response handler will never be called. What this means in
+practice is that you can cancel all of your pending requests in your activity's
+{@link android.app.Activity#onStop onStop()} method and you don't have to litter your
+response handlers with checks for {@code getActivity() == null},
+whether {@code onSaveInstanceState()} has been called already, or other defensive
+boilerplate.</p>
+
+<p>To take advantage of this behavior, you would typically have to
+track all in-flight requests in order to be able to cancel them at the
+appropriate time. There is an easier way: you can associate a tag object with each
+request. You can then use this tag to provide a scope of requests to cancel. For
+example, you can tag all of your requests with the {@link android.app.Activity} they
+are being made on behalf of, and call {@code requestQueue.cancelAll(this)} from
+{@link android.app.Activity#onStop onStop()}.
+Similarly, you could tag all thumbnail image requests in a
+{@link android.support.v4.view.ViewPager} tab with their respective tabs and cancel on swipe
+to make sure that the new tab isn't being held up by requests from another one.</p>
+
+<p>Here is an example that uses a string value for the tag:</p>
+
+<ol>
+<li>Define your tag and add it to your requests.
+<pre>
+public static final String TAG = "MyTag";
+StringRequest stringRequest; // Assume this exists.
+RequestQueue mRequestQueue; // Assume this exists.
+
+// Set the tag on the request.
+stringRequest.setTag(TAG);
+
+// Add the request to the RequestQueue.
+mRequestQueue.add(stringRequest);</pre>
+</li>
+
+<li>In your activity's {@link android.app.Activity#onStop onStop()} method, cancel all requests that have this tag.
+<pre>
+@Override
+protected void onStop () {
+ super.onStop();
+ if (mRequestQueue != null) {
+ mRequestQueue.cancelAll(TAG);
+ }
+}
+</pre></li></ol>
+
+<p>Take care when canceling requests. If you are depending on your response handler to
+advance a state or kick off another process, you need to account for this. Again, the
+response handler will not be called.
+</p>
diff --git a/docs/image_sources/training/volley/volley-request.graffle b/docs/image_sources/training/volley/volley-request.graffle
new file mode 100644
index 0000000..1d79b3df
--- /dev/null
+++ b/docs/image_sources/training/volley/volley-request.graffle
@@ -0,0 +1,2259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ActiveLayerIndex</key>
+ <integer>0</integer>
+ <key>ApplicationVersion</key>
+ <array>
+ <string>com.omnigroup.OmniGrafflePro</string>
+ <string>139.18.0.187838</string>
+ </array>
+ <key>AutoAdjust</key>
+ <true/>
+ <key>BackgroundGraphic</key>
+ <dict>
+ <key>Bounds</key>
+ <string>{{0, 0}, {576, 733}}</string>
+ <key>Class</key>
+ <string>SolidGraphic</string>
+ <key>ID</key>
+ <integer>2</integer>
+ <key>Style</key>
+ <dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>BaseZoom</key>
+ <integer>0</integer>
+ <key>CanvasOrigin</key>
+ <string>{0, 0}</string>
+ <key>ColumnAlign</key>
+ <integer>1</integer>
+ <key>ColumnSpacing</key>
+ <real>36</real>
+ <key>CreationDate</key>
+ <string>2014-03-24 22:38:51 +0000</string>
+ <key>Creator</key>
+ <string>Katie McCormick</string>
+ <key>DisplayScale</key>
+ <string>1 0/72 in = 1 0/72 in</string>
+ <key>GraphDocumentVersion</key>
+ <integer>8</integer>
+ <key>GraphicsList</key>
+ <array>
+ <dict>
+ <key>Bounds</key>
+ <string>{{68.798424333456921, 309.90064645900816}, {70.999998092651367, 24}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>Vertical</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>1990</integer>
+ <key>Rotation</key>
+ <real>88.863800048828125</real>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Align</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720
+
+\f0\fs24 \cf0 cache miss}</string>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{164.31081962585449, 233}, {59, 24}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>1989</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Align</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720
+
+\f0\fs24 \cf0 cache hit}</string>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{109, 567.5}, {72, 24}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FitText</key>
+ <string>YES</string>
+ <key>Flow</key>
+ <string>Resize</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Helvetica</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>1987</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Align</key>
+ <integer>0</integer>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720
+
+\f0\fs24 \cf0 round-robin}</string>
+ </dict>
+ <key>Wrap</key>
+ <string>NO</string>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>209</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1986</integer>
+ <key>Points</key>
+ <array>
+ <string>{362.483096786499, 520.41741752624512}</string>
+ <string>{439.59458923339844, 165.16970062255859}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>213</integer>
+ <key>Info</key>
+ <integer>7</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>209</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1985</integer>
+ <key>Points</key>
+ <array>
+ <string>{362.483096786499, 461.33483529090881}</string>
+ <string>{439.59458923339844, 165.16970062255859}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>204</integer>
+ <key>Info</key>
+ <integer>7</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>209</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1984</integer>
+ <key>Points</key>
+ <array>
+ <string>{362.48308152770994, 402.2522608306885}</string>
+ <string>{439.59458923339844, 165.16970062255859}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>203</integer>
+ <key>Info</key>
+ <integer>7</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>209</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1983</integer>
+ <key>Points</key>
+ <array>
+ <string>{294.65540856933592, 227.99996948242188}</string>
+ <string>{381.3049268594778, 164.77353614247437}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>210</integer>
+ <key>Info</key>
+ <integer>6</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>4</integer>
+ <key>Info</key>
+ <integer>6</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1982</integer>
+ <key>Points</key>
+ <array>
+ <string>{94.216227905273399, 165.16970062255859}</string>
+ <string>{92.966230102539043, 227.99999237060547}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>157</integer>
+ <key>Info</key>
+ <integer>5</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>213</integer>
+ <key>Info</key>
+ <integer>9</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1980</integer>
+ <key>Points</key>
+ <array>
+ <string>{179.26087951660151, 462.43391850741568}</string>
+ <string>{224.48312730407713, 514.92974541704564}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>1973</integer>
+ <key>Info</key>
+ <integer>3</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>204</integer>
+ <key>Info</key>
+ <integer>8</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1979</integer>
+ <key>Points</key>
+ <array>
+ <string>{179.76073189455099, 462.42177007636207}</string>
+ <string>{224.48312730407716, 461.33483529090881}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>1973</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>203</integer>
+ <key>Info</key>
+ <integer>8</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1978</integer>
+ <key>Points</key>
+ <array>
+ <string>{179.26087951660151, 462.43391850741568}</string>
+ <string>{224.48311204528804, 402.2522608306885}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>1973</integer>
+ <key>Info</key>
+ <integer>3</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>1973</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1975</integer>
+ <key>Points</key>
+ <array>
+ <string>{92.966230102539043, 286.16969299316406}</string>
+ <string>{92.966239929199205, 356.41910654608148}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>4</integer>
+ <key>Info</key>
+ <integer>5</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>LineGraphic</string>
+ <key>Head</key>
+ <dict>
+ <key>ID</key>
+ <integer>210</integer>
+ <key>Info</key>
+ <integer>8</integer>
+ </dict>
+ <key>ID</key>
+ <integer>1974</integer>
+ <key>Points</key>
+ <array>
+ <string>{150.81081933593748, 257.08484268188477}</string>
+ <string>{236.81081933593748, 257.08481979370117}</string>
+ </array>
+ <key>Style</key>
+ <dict>
+ <key>stroke</key>
+ <dict>
+ <key>HeadArrow</key>
+ <string>FilledArrow</string>
+ <key>Legacy</key>
+ <true/>
+ <key>TailArrow</key>
+ <string>0</string>
+ </dict>
+ </dict>
+ <key>Tail</key>
+ <dict>
+ <key>ID</key>
+ <integer>4</integer>
+ <key>Info</key>
+ <integer>7</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{391, 540.66744232177734}, {137.99996948242188, 23.499996185302734}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>214</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.2</string>
+ <key>g</key>
+ <string>0.733333</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.131021</string>
+ <key>g</key>
+ <string>0.363196</string>
+ <key>r</key>
+ <string>0.725948</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 network threads}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{224.48312730407713, 508.66741943359375}, {137.99996948242188, 23.499996185302734}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>213</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.2</string>
+ <key>g</key>
+ <string>0.733333</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.131021</string>
+ <key>g</key>
+ <string>0.363196</string>
+ <key>r</key>
+ <string>0.725948</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 HTTP...}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{23.966230102539043, 498}, {137.99998474121094, 58.169700622558594}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>212</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.2</string>
+ <key>g</key>
+ <string>0.733333</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.131021</string>
+ <key>g</key>
+ <string>0.363196</string>
+ <key>r</key>
+ <string>0.725948</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Request dequeued by NetworkDispatcher}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{23.966245361328106, 432.24999237060547}, {137.99998474121094, 58.169700622558594}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>211</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.2</string>
+ <key>g</key>
+ <string>0.733333</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.131021</string>
+ <key>g</key>
+ <string>0.363196</string>
+ <key>r</key>
+ <string>0.725948</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Request dequeued by NetworkDispatcher}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{236.81081933593748, 227.99996948242188}, {115.68917846679688, 58.169700622558594}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>10</real>
+ </dict>
+ <key>ID</key>
+ <integer>210</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.8</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.4</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Request read from cache and parsed}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{380.5, 107}, {118.18917846679688, 58.169700622558594}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>209</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.898039</string>
+ <key>g</key>
+ <string>0.709804</string>
+ <key>r</key>
+ <string>0.2</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.93512</string>
+ <key>g</key>
+ <string>0.472602</string>
+ <key>r</key>
+ <string>0.333854</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Parsed response delivered on main thread}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{391, 508.66740417480469}, {137.99996948242188, 23.499996185302734}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>10</real>
+ </dict>
+ <key>ID</key>
+ <integer>207</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.8</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.4</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 cache thread}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{391, 476.6673583984375}, {137.99996948242188, 23.499980926513672}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>206</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.898039</string>
+ <key>g</key>
+ <string>0.709804</string>
+ <key>r</key>
+ <string>0.2</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.93512</string>
+ <key>g</key>
+ <string>0.472602</string>
+ <key>r</key>
+ <string>0.333854</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 main thread}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{224.48312730407713, 421.74998497962952}, {137.99996948242188, 79.169700622558594}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>204</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.2</string>
+ <key>g</key>
+ <string>0.733333</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.131021</string>
+ <key>g</key>
+ <string>0.363196</string>
+ <key>r</key>
+ <string>0.725948</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 HTTP transaction, response parse, cache write (if applicable)}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{224.48311204528807, 390.50226273803713}, {137.99996948242188, 23.499996185302734}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>203</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.2</string>
+ <key>g</key>
+ <string>0.733333</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.131021</string>
+ <key>g</key>
+ <string>0.363196</string>
+ <key>r</key>
+ <string>0.725948</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 HTTP...}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{23.966245361328106, 366.5}, {137.99998474121094, 58.169700622558594}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>200</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.2</string>
+ <key>g</key>
+ <string>0.733333</string>
+ <key>r</key>
+ <string>1</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.131021</string>
+ <key>g</key>
+ <string>0.363196</string>
+ <key>r</key>
+ <string>0.725948</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Request dequeued by NetworkDispatcher}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{35.121640869140606, 227.99999237060547}, {115.68917846679688, 58.169700622558594}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>10</real>
+ </dict>
+ <key>ID</key>
+ <integer>4</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.8</string>
+ <key>r</key>
+ <string>0.6</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0.6</string>
+ <key>r</key>
+ <string>0.4</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Request dequeued by CacheDispatcher}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{35.121638671874962, 107}, {118.18917846679688, 58.169700622558594}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>157</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{1, 1}</string>
+ <string>{1, -1}</string>
+ <string>{-1, -1}</string>
+ <string>{-1, 1}</string>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ <string>{-0.5, -0.233518}</string>
+ <string>{-0.49144199999999999, 0.26006299999999999}</string>
+ <string>{0.50711799999999996, -0.22408600000000001}</string>
+ <string>{0.50711799999999996, 0.267179}</string>
+ <string>{-0.27431, -0.474028}</string>
+ <string>{0.27977999999999997, -0.47847800000000001}</string>
+ <string>{0.29393799999999998, 0.54304399999999997}</string>
+ <string>{-0.28623199999999999, 0.55380399999999996}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.898039</string>
+ <key>g</key>
+ <string>0.709804</string>
+ <key>r</key>
+ <string>0.2</string>
+ </dict>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.35</string>
+ <key>b</key>
+ <string>0.328823</string>
+ <key>g</key>
+ <string>0.328823</string>
+ <key>r</key>
+ <string>0.328823</string>
+ </dict>
+ <key>Fuzziness</key>
+ <real>1.5349206924438477</real>
+ <key>ShadowVector</key>
+ <string>{0, 1}</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.93512</string>
+ <key>g</key>
+ <string>0.472602</string>
+ <key>r</key>
+ <string>0.333854</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>2</real>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Text</key>
+ <string>{\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf190
+\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc
+
+\f0\b\fs24 \cf0 Request added to queue in priority order}</string>
+ <key>VerticalPad</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Class</key>
+ <string>Group</string>
+ <key>Graphics</key>
+ <array>
+ <dict>
+ <key>Bounds</key>
+ <string>{{20.00199264625337, 331.00000000000006}, {145.92853034472586, 25.419100784950434}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.65</string>
+ <key>b</key>
+ <string>0</string>
+ <key>g</key>
+ <string>0</string>
+ <key>r</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-Condensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>1972</integer>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>Pad</key>
+ <integer>2</integer>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
+ <string>{{6.671600341796875, 356.41910654608148}, {172.58927917480466, 212.02962392266838}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>FontInfo</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>a</key>
+ <string>0.65</string>
+ <key>w</key>
+ <string>0</string>
+ </dict>
+ <key>Font</key>
+ <string>Roboto-BoldCondensed</string>
+ <key>Size</key>
+ <real>12</real>
+ </dict>
+ <key>ID</key>
+ <integer>1973</integer>
+ <key>Magnets</key>
+ <array>
+ <string>{0, 1}</string>
+ <string>{0, -1}</string>
+ <string>{1, 0}</string>
+ <string>{-1, 0}</string>
+ </array>
+ <key>Shape</key>
+ <string>Rectangle</string>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Color</key>
+ <dict>
+ <key>b</key>
+ <string>0.578326</string>
+ <key>g</key>
+ <string>0.578615</string>
+ <key>r</key>
+ <string>0.578453</string>
+ </dict>
+ <key>CornerRadius</key>
+ <real>5</real>
+ <key>Pattern</key>
+ <integer>1</integer>
+ </dict>
+ </dict>
+ <key>Text</key>
+ <dict>
+ <key>VerticalPad</key>
+ <integer>10</integer>
+ </dict>
+ <key>TextPlacement</key>
+ <integer>0</integer>
+ </dict>
+ </array>
+ <key>ID</key>
+ <integer>1971</integer>
+ </dict>
+ </array>
+ <key>GridInfo</key>
+ <dict/>
+ <key>GuidesLocked</key>
+ <string>NO</string>
+ <key>GuidesVisible</key>
+ <string>YES</string>
+ <key>HPages</key>
+ <integer>1</integer>
+ <key>ImageCounter</key>
+ <integer>1</integer>
+ <key>KeepToScale</key>
+ <false/>
+ <key>Layers</key>
+ <array>
+ <dict>
+ <key>Lock</key>
+ <string>NO</string>
+ <key>Name</key>
+ <string>Layer 1</string>
+ <key>Print</key>
+ <string>YES</string>
+ <key>View</key>
+ <string>YES</string>
+ </dict>
+ </array>
+ <key>LayoutInfo</key>
+ <dict>
+ <key>Animate</key>
+ <string>NO</string>
+ <key>circoMinDist</key>
+ <real>18</real>
+ <key>circoSeparation</key>
+ <real>0.0</real>
+ <key>layoutEngine</key>
+ <string>dot</string>
+ <key>neatoSeparation</key>
+ <real>0.0</real>
+ <key>twopiSeparation</key>
+ <real>0.0</real>
+ </dict>
+ <key>LinksVisible</key>
+ <string>NO</string>
+ <key>MagnetsVisible</key>
+ <string>NO</string>
+ <key>MasterSheets</key>
+ <array/>
+ <key>ModificationDate</key>
+ <string>2014-03-24 23:38:43 +0000</string>
+ <key>Modifier</key>
+ <string>Katie McCormick</string>
+ <key>NotesVisible</key>
+ <string>NO</string>
+ <key>Orientation</key>
+ <integer>2</integer>
+ <key>OriginVisible</key>
+ <string>NO</string>
+ <key>PageBreaks</key>
+ <string>YES</string>
+ <key>PrintInfo</key>
+ <dict>
+ <key>NSBottomMargin</key>
+ <array>
+ <string>float</string>
+ <string>41</string>
+ </array>
+ <key>NSHorizonalPagination</key>
+ <array>
+ <string>coded</string>
+ <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string>
+ </array>
+ <key>NSLeftMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSPaperSize</key>
+ <array>
+ <string>size</string>
+ <string>{612, 792}</string>
+ </array>
+ <key>NSPrintReverseOrientation</key>
+ <array>
+ <string>int</string>
+ <string>0</string>
+ </array>
+ <key>NSRightMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ <key>NSTopMargin</key>
+ <array>
+ <string>float</string>
+ <string>18</string>
+ </array>
+ </dict>
+ <key>PrintOnePage</key>
+ <false/>
+ <key>ReadOnly</key>
+ <string>NO</string>
+ <key>RowAlign</key>
+ <integer>1</integer>
+ <key>RowSpacing</key>
+ <real>36</real>
+ <key>SheetTitle</key>
+ <string>Canvas 1</string>
+ <key>SmartAlignmentGuidesActive</key>
+ <string>YES</string>
+ <key>SmartDistanceGuidesActive</key>
+ <string>YES</string>
+ <key>UniqueID</key>
+ <integer>1</integer>
+ <key>UseEntirePage</key>
+ <false/>
+ <key>VPages</key>
+ <integer>1</integer>
+ <key>WindowInfo</key>
+ <dict>
+ <key>CurrentSheet</key>
+ <integer>0</integer>
+ <key>ExpandedCanvases</key>
+ <array>
+ <dict>
+ <key>name</key>
+ <string>Canvas 1</string>
+ </dict>
+ </array>
+ <key>Frame</key>
+ <string>{{159, 135}, {899, 874}}</string>
+ <key>ListView</key>
+ <true/>
+ <key>OutlineWidth</key>
+ <integer>142</integer>
+ <key>RightSidebar</key>
+ <false/>
+ <key>ShowRuler</key>
+ <true/>
+ <key>Sidebar</key>
+ <true/>
+ <key>SidebarWidth</key>
+ <integer>120</integer>
+ <key>VisibleRegion</key>
+ <string>{{-94, -1}, {764, 735}}</string>
+ <key>Zoom</key>
+ <real>1</real>
+ <key>ZoomValues</key>
+ <array>
+ <array>
+ <string>Canvas 1</string>
+ <real>1</real>
+ <real>1</real>
+ </array>
+ </array>
+ </dict>
+</dict>
+</plist>
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index de8531b..d321baf 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -233,7 +233,7 @@
static void android_drm_DrmManagerClient_release(
JNIEnv* env, jobject thiz, jint uniqueId) {
ALOGV("release - Enter");
- DrmManagerClientImpl::remove(uniqueId);
+ getDrmManagerClientImpl(env, thiz)->remove(uniqueId);
getDrmManagerClientImpl(env, thiz)->setOnInfoListener(uniqueId, NULL);
sp<DrmManagerClientImpl> oldClient = setDrmManagerClientImpl(env, thiz, NULL);
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 06cf253..ef0a411 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -194,6 +194,11 @@
* while {@link #getAllocationByteCount()} will reflect that of the initial
* configuration.</p>
*
+ * <p>Note: This may change this result of hasAlpha(). When converting to 565,
+ * the new bitmap will always be considered opaque. When converting from 565,
+ * the new bitmap will be considered non-opaque, and will respect the value
+ * set by setPremultiplied().</p>
+ *
* <p>WARNING: This method should NOT be called on a bitmap currently used
* by the view system. It does not make guarantees about how the underlying
* pixel buffer is remapped to the new config, just that the allocation is
@@ -217,7 +222,8 @@
throw new IllegalStateException("native-backed bitmaps may not be reconfigured");
}
- nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length);
+ nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length,
+ mIsPremultiplied);
mWidth = width;
mHeight = height;
}
@@ -1586,7 +1592,8 @@
private static native void nativeDestructor(long nativeBitmap);
private static native boolean nativeRecycle(long nativeBitmap);
private static native void nativeReconfigure(long nativeBitmap, int width, int height,
- int config, int allocSize);
+ int config, int allocSize,
+ boolean isPremultiplied);
private static native boolean nativeCompress(long nativeBitmap, int format,
int quality, OutputStream stream,
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index c20502f..bc20ea5 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -260,7 +260,11 @@
public boolean inScaled;
/**
- * If this is set to true, then the resulting bitmap will allocate its
+ * @deprecated As of {@link android.os.Build.VERSION_CODES#L}, this is
+ * ignored.
+ *
+ * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, if this
+ * is set to true, then the resulting bitmap will allocate its
* pixels such that they can be purged if the system needs to reclaim
* memory. In that instance, when the pixels need to be accessed again
* (e.g. the bitmap is drawn, getPixels() is called), they will be
@@ -287,14 +291,20 @@
* android.graphics.BitmapFactory.Options)} or {@link #decodeFile(String,
* android.graphics.BitmapFactory.Options)}.</p>
*/
+ @Deprecated
public boolean inPurgeable;
/**
- * This field works in conjuction with inPurgeable. If inPurgeable is
- * false, then this field is ignored. If inPurgeable is true, then this
- * field determines whether the bitmap can share a reference to the
- * input data (inputstream, array, etc.) or if it must make a deep copy.
+ * @deprecated As of {@link android.os.Build.VERSION_CODES#L}, this is
+ * ignored.
+ *
+ * In {@link android.os.Build.VERSION_CODES#KITKAT} and below, this
+ * field works in conjuction with inPurgeable. If inPurgeable is false,
+ * then this field is ignored. If inPurgeable is true, then this field
+ * determines whether the bitmap can share a reference to the input
+ * data (inputstream, array, etc.) or if it must make a deep copy.
*/
+ @Deprecated
public boolean inInputShareable;
/**
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index bd868f2..13789ca 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -16,11 +16,17 @@
package android.graphics;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.text.GraphicsOperations;
import android.text.SpannableString;
import android.text.SpannedString;
import android.text.TextUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
import javax.microedition.khronos.opengles.GL;
/**
@@ -136,7 +142,7 @@
*
* @param bitmap Specifies a mutable bitmap for the canvas to draw into.
*/
- public Canvas(Bitmap bitmap) {
+ public Canvas(@NonNull Bitmap bitmap) {
if (!bitmap.isMutable()) {
throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
}
@@ -192,7 +198,7 @@
* @see #setDensity(int)
* @see #getDensity()
*/
- public void setBitmap(Bitmap bitmap) {
+ public void setBitmap(@Nullable Bitmap bitmap) {
if (isHardwareAccelerated()) {
throw new RuntimeException("Can't set a bitmap device on a GL canvas");
}
@@ -242,21 +248,27 @@
*
* @return true if the device that the current layer draws into is opaque
*/
- public native boolean isOpaque();
+ public boolean isOpaque() {
+ return native_isOpaque(mNativeCanvasWrapper);
+ }
/**
* Returns the width of the current drawing layer
*
* @return the width of the current drawing layer
*/
- public native int getWidth();
+ public int getWidth() {
+ return native_getWidth(mNativeCanvasWrapper);
+ }
/**
* Returns the height of the current drawing layer
*
* @return the height of the current drawing layer
*/
- public native int getHeight();
+ public int getHeight() {
+ return native_getHeight(mNativeCanvasWrapper);
+ }
/**
* <p>Returns the target density of the canvas. The default density is
@@ -321,6 +333,19 @@
// the SAVE_FLAG constants must match their native equivalents
+ /** @hide */
+ @IntDef(flag = true,
+ value = {
+ MATRIX_SAVE_FLAG,
+ CLIP_SAVE_FLAG,
+ HAS_ALPHA_LAYER_SAVE_FLAG,
+ FULL_COLOR_LAYER_SAVE_FLAG,
+ CLIP_TO_LAYER_SAVE_FLAG,
+ ALL_SAVE_FLAG
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Saveflags {}
+
/** restore the current matrix when restore() is called */
public static final int MATRIX_SAVE_FLAG = 0x01;
/** restore the current clip when restore() is called */
@@ -343,7 +368,9 @@
*
* @return The value to pass to restoreToCount() to balance this save()
*/
- public native int save();
+ public int save() {
+ return native_save(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
+ }
/**
* Based on saveFlags, can save the current matrix and clip onto a private
@@ -356,7 +383,9 @@
* to save/restore
* @return The value to pass to restoreToCount() to balance this save()
*/
- public native int save(int saveFlags);
+ public int save(@Saveflags int saveFlags) {
+ return native_save(mNativeCanvasWrapper, saveFlags);
+ }
/**
* This behaves the same as save(), but in addition it allocates an
@@ -374,8 +403,9 @@
* @param saveFlags see _SAVE_FLAG constants
* @return value to pass to restoreToCount() to balance this save()
*/
- public int saveLayer(RectF bounds, Paint paint, int saveFlags) {
- return native_saveLayer(mNativeCanvasWrapper, bounds,
+ public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint, @Saveflags int saveFlags) {
+ return native_saveLayer(mNativeCanvasWrapper,
+ bounds.left, bounds.top, bounds.right, bounds.bottom,
paint != null ? paint.mNativePaint : 0,
saveFlags);
}
@@ -383,15 +413,15 @@
/**
* Convenience for saveLayer(bounds, paint, {@link #ALL_SAVE_FLAG})
*/
- public int saveLayer(RectF bounds, Paint paint) {
+ public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint) {
return saveLayer(bounds, paint, ALL_SAVE_FLAG);
}
/**
* Helper version of saveLayer() that takes 4 values rather than a RectF.
*/
- public int saveLayer(float left, float top, float right, float bottom, Paint paint,
- int saveFlags) {
+ public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint,
+ @Saveflags int saveFlags) {
return native_saveLayer(mNativeCanvasWrapper, left, top, right, bottom,
paint != null ? paint.mNativePaint : 0,
saveFlags);
@@ -400,7 +430,7 @@
/**
* Convenience for saveLayer(left, top, right, bottom, paint, {@link #ALL_SAVE_FLAG})
*/
- public int saveLayer(float left, float top, float right, float bottom, Paint paint) {
+ public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint) {
return saveLayer(left, top, right, bottom, paint, ALL_SAVE_FLAG);
}
@@ -420,15 +450,17 @@
* @param saveFlags see _SAVE_FLAG constants
* @return value to pass to restoreToCount() to balance this call
*/
- public int saveLayerAlpha(RectF bounds, int alpha, int saveFlags) {
+ public int saveLayerAlpha(@NonNull RectF bounds, int alpha, @Saveflags int saveFlags) {
alpha = Math.min(255, Math.max(0, alpha));
- return native_saveLayerAlpha(mNativeCanvasWrapper, bounds, alpha, saveFlags);
+ return native_saveLayerAlpha(mNativeCanvasWrapper,
+ bounds.left, bounds.top, bounds.right, bounds.bottom,
+ alpha, saveFlags);
}
/**
* Convenience for saveLayerAlpha(bounds, alpha, {@link #ALL_SAVE_FLAG})
*/
- public int saveLayerAlpha(RectF bounds, int alpha) {
+ public int saveLayerAlpha(@NonNull RectF bounds, int alpha) {
return saveLayerAlpha(bounds, alpha, ALL_SAVE_FLAG);
}
@@ -436,7 +468,7 @@
* Helper for saveLayerAlpha() that takes 4 values instead of a RectF.
*/
public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
- int saveFlags) {
+ @Saveflags int saveFlags) {
return native_saveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom,
alpha, saveFlags);
}
@@ -453,13 +485,17 @@
* modifications to the matrix/clip state since the last save call. It is
* an error to call restore() more times than save() was called.
*/
- public native void restore();
+ public void restore() {
+ native_restore(mNativeCanvasWrapper);
+ }
/**
* Returns the number of matrix/clip states on the Canvas' private stack.
* This will equal # save() calls - # restore() calls.
*/
- public native int getSaveCount();
+ public int getSaveCount() {
+ return native_getSaveCount(mNativeCanvasWrapper);
+ }
/**
* Efficient way to pop any calls to save() that happened after the save
@@ -474,7 +510,9 @@
*
* @param saveCount The save level to restore to.
*/
- public native void restoreToCount(int saveCount);
+ public void restoreToCount(int saveCount) {
+ native_restoreToCount(mNativeCanvasWrapper, saveCount);
+ }
/**
* Preconcat the current matrix with the specified translation
@@ -482,7 +520,9 @@
* @param dx The distance to translate in X
* @param dy The distance to translate in Y
*/
- public native void translate(float dx, float dy);
+ public void translate(float dx, float dy) {
+ native_translate(mNativeCanvasWrapper, dx, dy);
+ }
/**
* Preconcat the current matrix with the specified scale.
@@ -490,7 +530,9 @@
* @param sx The amount to scale in X
* @param sy The amount to scale in Y
*/
- public native void scale(float sx, float sy);
+ public void scale(float sx, float sy) {
+ native_scale(mNativeCanvasWrapper, sx, sy);
+ }
/**
* Preconcat the current matrix with the specified scale.
@@ -511,7 +553,9 @@
*
* @param degrees The amount to rotate, in degrees
*/
- public native void rotate(float degrees);
+ public void rotate(float degrees) {
+ native_rotate(mNativeCanvasWrapper, degrees);
+ }
/**
* Preconcat the current matrix with the specified rotation.
@@ -532,7 +576,9 @@
* @param sx The amount to skew in X
* @param sy The amount to skew in Y
*/
- public native void skew(float sx, float sy);
+ public void skew(float sx, float sy) {
+ native_skew(mNativeCanvasWrapper, sx, sy);
+ }
/**
* Preconcat the current matrix with the specified matrix. If the specified
@@ -540,7 +586,7 @@
*
* @param matrix The matrix to preconcatenate with the current matrix
*/
- public void concat(Matrix matrix) {
+ public void concat(@Nullable Matrix matrix) {
if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance);
}
@@ -557,7 +603,7 @@
*
* @see #concat(Matrix)
*/
- public void setMatrix(Matrix matrix) {
+ public void setMatrix(@Nullable Matrix matrix) {
native_setMatrix(mNativeCanvasWrapper,
matrix == null ? 0 : matrix.native_instance);
}
@@ -567,7 +613,7 @@
* the matrix in the canvas, but just returns a copy of it.
*/
@Deprecated
- public void getMatrix(Matrix ctm) {
+ public void getMatrix(@NonNull Matrix ctm) {
native_getCTM(mNativeCanvasWrapper, ctm.native_instance);
}
@@ -576,7 +622,7 @@
* matrix.
*/
@Deprecated
- public final Matrix getMatrix() {
+ public final @NonNull Matrix getMatrix() {
Matrix m = new Matrix();
//noinspection deprecation
getMatrix(m);
@@ -590,7 +636,7 @@
* @param op How the clip is modified
* @return true if the resulting clip is non-empty
*/
- public boolean clipRect(RectF rect, Region.Op op) {
+ public boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) {
return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
op.nativeInt);
}
@@ -603,7 +649,7 @@
* @param op How the clip is modified
* @return true if the resulting clip is non-empty
*/
- public boolean clipRect(Rect rect, Region.Op op) {
+ public boolean clipRect(@NonNull Rect rect, @NonNull Region.Op op) {
return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
op.nativeInt);
}
@@ -615,8 +661,11 @@
* @param rect The rectangle to intersect with the current clip.
* @return true if the resulting clip is non-empty
*/
- public native boolean clipRect(RectF rect);
-
+ public boolean clipRect(@NonNull RectF rect) {
+ return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
+ Region.Op.INTERSECT.nativeInt);
+ }
+
/**
* Intersect the current clip with the specified rectangle, which is
* expressed in local coordinates.
@@ -624,7 +673,10 @@
* @param rect The rectangle to intersect with the current clip.
* @return true if the resulting clip is non-empty
*/
- public native boolean clipRect(Rect rect);
+ public boolean clipRect(@NonNull Rect rect) {
+ return native_clipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
+ Region.Op.INTERSECT.nativeInt);
+ }
/**
* Modify the current clip with the specified rectangle, which is
@@ -641,7 +693,8 @@
* @param op How the clip is modified
* @return true if the resulting clip is non-empty
*/
- public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) {
+ public boolean clipRect(float left, float top, float right, float bottom,
+ @NonNull Region.Op op) {
return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt);
}
@@ -658,7 +711,10 @@
* clip
* @return true if the resulting clip is non-empty
*/
- public native boolean clipRect(float left, float top, float right, float bottom);
+ public boolean clipRect(float left, float top, float right, float bottom) {
+ return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom,
+ Region.Op.INTERSECT.nativeInt);
+ }
/**
* Intersect the current clip with the specified rectangle, which is
@@ -673,7 +729,10 @@
* clip
* @return true if the resulting clip is non-empty
*/
- public native boolean clipRect(int left, int top, int right, int bottom);
+ public boolean clipRect(int left, int top, int right, int bottom) {
+ return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom,
+ Region.Op.INTERSECT.nativeInt);
+ }
/**
* Modify the current clip with the specified path.
@@ -682,7 +741,7 @@
* @param op How the clip is modified
* @return true if the resulting is non-empty
*/
- public boolean clipPath(Path path, Region.Op op) {
+ public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
return native_clipPath(mNativeCanvasWrapper, path.ni(), op.nativeInt);
}
@@ -692,7 +751,7 @@
* @param path The path to intersect with the current clip
* @return true if the resulting is non-empty
*/
- public boolean clipPath(Path path) {
+ public boolean clipPath(@NonNull Path path) {
return clipPath(path, Region.Op.INTERSECT);
}
@@ -710,7 +769,7 @@
* @deprecated Unlike all other clip calls this API does not respect the
* current matrix. Use {@link #clipRect(Rect)} as an alternative.
*/
- public boolean clipRegion(Region region, Region.Op op) {
+ public boolean clipRegion(@NonNull Region region, @NonNull Region.Op op) {
return native_clipRegion(mNativeCanvasWrapper, region.ni(), op.nativeInt);
}
@@ -727,15 +786,15 @@
* @deprecated Unlike all other clip calls this API does not respect the
* current matrix. Use {@link #clipRect(Rect)} as an alternative.
*/
- public boolean clipRegion(Region region) {
+ public boolean clipRegion(@NonNull Region region) {
return clipRegion(region, Region.Op.INTERSECT);
}
- public DrawFilter getDrawFilter() {
+ public @Nullable DrawFilter getDrawFilter() {
return mDrawFilter;
}
- public void setDrawFilter(DrawFilter filter) {
+ public void setDrawFilter(@Nullable DrawFilter filter) {
long nativeFilter = 0;
if (filter != null) {
nativeFilter = filter.mNativeInt;
@@ -779,8 +838,9 @@
* @return true if the rect (transformed by the canvas' matrix)
* does not intersect with the canvas' clip
*/
- public boolean quickReject(RectF rect, EdgeType type) {
- return native_quickReject(mNativeCanvasWrapper, rect);
+ public boolean quickReject(@NonNull RectF rect, @NonNull EdgeType type) {
+ return native_quickReject(mNativeCanvasWrapper,
+ rect.left, rect.top, rect.right, rect.bottom);
}
/**
@@ -798,7 +858,7 @@
* @return true if the path (transformed by the canvas' matrix)
* does not intersect with the canvas' clip
*/
- public boolean quickReject(Path path, EdgeType type) {
+ public boolean quickReject(@NonNull Path path, @NonNull EdgeType type) {
return native_quickReject(mNativeCanvasWrapper, path.ni());
}
@@ -823,7 +883,7 @@
* does not intersect with the canvas' clip
*/
public boolean quickReject(float left, float top, float right, float bottom,
- EdgeType type) {
+ @NonNull EdgeType type) {
return native_quickReject(mNativeCanvasWrapper, left, top, right, bottom);
}
@@ -837,7 +897,7 @@
* still return true if the current clip is non-empty.
* @return true if the current clip is non-empty.
*/
- public boolean getClipBounds(Rect bounds) {
+ public boolean getClipBounds(@Nullable Rect bounds) {
return native_getClipBounds(mNativeCanvasWrapper, bounds);
}
@@ -846,7 +906,7 @@
*
* @return the clip bounds, or [0, 0, 0, 0] if the clip is empty.
*/
- public final Rect getClipBounds() {
+ public final @NonNull Rect getClipBounds() {
Rect r = new Rect();
getClipBounds(r);
return r;
@@ -894,7 +954,7 @@
* @param color the color to draw with
* @param mode the porter-duff mode to apply to the color
*/
- public void drawColor(int color, PorterDuff.Mode mode) {
+ public void drawColor(int color, @NonNull PorterDuff.Mode mode) {
native_drawColor(mNativeCanvasWrapper, color, mode.nativeInt);
}
@@ -905,7 +965,7 @@
*
* @param paint The paint used to draw onto the canvas
*/
- public void drawPaint(Paint paint) {
+ public void drawPaint(@NonNull Paint paint) {
native_drawPaint(mNativeCanvasWrapper, paint.mNativePaint);
}
@@ -925,19 +985,23 @@
* "points" that are drawn is really (count >> 1).
* @param paint The paint used to draw the points
*/
- public native void drawPoints(float[] pts, int offset, int count, Paint paint);
+ public void drawPoints(float[] pts, int offset, int count, @NonNull Paint paint) {
+ native_drawPoints(mNativeCanvasWrapper, pts, offset, count, paint.mNativePaint);
+ }
/**
* Helper for drawPoints() that assumes you want to draw the entire array
*/
- public void drawPoints(float[] pts, Paint paint) {
+ public void drawPoints(@NonNull float[] pts, @NonNull Paint paint) {
drawPoints(pts, 0, pts.length, paint);
}
/**
* Helper for drawPoints() for drawing a single point.
*/
- public native void drawPoint(float x, float y, Paint paint);
+ public void drawPoint(float x, float y, @NonNull Paint paint) {
+ native_drawPoint(mNativeCanvasWrapper, x, y, paint.mNativePaint);
+ }
/**
* Draw a line segment with the specified start and stop x,y coordinates,
@@ -951,7 +1015,8 @@
* @param startY The y-coordinate of the start point of the line
* @param paint The paint used to draw the line
*/
- public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
+ public void drawLine(float startX, float startY, float stopX, float stopY,
+ @NonNull Paint paint) {
native_drawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.mNativePaint);
}
@@ -970,9 +1035,11 @@
* (count >> 2).
* @param paint The paint used to draw the points
*/
- public native void drawLines(float[] pts, int offset, int count, Paint paint);
+ public void drawLines(float[] pts, int offset, int count, Paint paint) {
+ native_drawLines(mNativeCanvasWrapper, pts, offset, count, paint.mNativePaint);
+ }
- public void drawLines(float[] pts, Paint paint) {
+ public void drawLines(@NonNull float[] pts, @NonNull Paint paint) {
drawLines(pts, 0, pts.length, paint);
}
@@ -983,8 +1050,9 @@
* @param rect The rect to be drawn
* @param paint The paint used to draw the rect
*/
- public void drawRect(RectF rect, Paint paint) {
- native_drawRect(mNativeCanvasWrapper, rect, paint.mNativePaint);
+ public void drawRect(@NonNull RectF rect, @NonNull Paint paint) {
+ native_drawRect(mNativeCanvasWrapper,
+ rect.left, rect.top, rect.right, rect.bottom, paint.mNativePaint);
}
/**
@@ -994,7 +1062,7 @@
* @param r The rectangle to be drawn.
* @param paint The paint used to draw the rectangle
*/
- public void drawRect(Rect r, Paint paint) {
+ public void drawRect(@NonNull Rect r, @NonNull Paint paint) {
drawRect(r.left, r.top, r.right, r.bottom, paint);
}
@@ -1009,7 +1077,7 @@
* @param bottom The bottom side of the rectangle to be drawn
* @param paint The paint used to draw the rect
*/
- public void drawRect(float left, float top, float right, float bottom, Paint paint) {
+ public void drawRect(float left, float top, float right, float bottom, @NonNull Paint paint) {
native_drawRect(mNativeCanvasWrapper, left, top, right, bottom, paint.mNativePaint);
}
@@ -1019,7 +1087,7 @@
*
* @param oval The rectangle bounds of the oval to be drawn
*/
- public void drawOval(RectF oval, Paint paint) {
+ public void drawOval(@NonNull RectF oval, @NonNull Paint paint) {
if (oval == null) {
throw new NullPointerException();
}
@@ -1036,7 +1104,7 @@
* @param radius The radius of the cirle to be drawn
* @param paint The paint used to draw the circle
*/
- public void drawCircle(float cx, float cy, float radius, Paint paint) {
+ public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
native_drawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.mNativePaint);
}
@@ -1063,8 +1131,8 @@
close it if it is being stroked. This will draw a wedge
* @param paint The paint used to draw the arc
*/
- public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter,
- Paint paint) {
+ public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,
+ @NonNull Paint paint) {
if (oval == null) {
throw new NullPointerException();
}
@@ -1081,7 +1149,7 @@
* @param ry The y-radius of the oval used to round the corners
* @param paint The paint used to draw the roundRect
*/
- public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
+ public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
}
@@ -1094,7 +1162,7 @@
* @param paint The paint used to draw the roundRect
*/
public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
- Paint paint) {
+ @NonNull Paint paint) {
native_drawRoundRect(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, paint.mNativePaint);
}
@@ -1105,7 +1173,7 @@
* @param path The path to be drawn
* @param paint The paint used to draw the path
*/
- public void drawPath(Path path, Paint paint) {
+ public void drawPath(@NonNull Path path, @NonNull Paint paint) {
native_drawPath(mNativeCanvasWrapper, path.ni(), paint.mNativePaint);
}
@@ -1132,7 +1200,7 @@
*
* @hide
*/
- public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
+ public void drawPatch(@NonNull NinePatch patch, @NonNull Rect dst, @Nullable Paint paint) {
patch.drawSoftware(this, dst, paint);
}
@@ -1145,7 +1213,7 @@
*
* @hide
*/
- public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
+ public void drawPatch(@NonNull NinePatch patch, @NonNull RectF dst, @Nullable Paint paint) {
patch.drawSoftware(this, dst, paint);
}
@@ -1168,7 +1236,7 @@
* @param top The position of the top side of the bitmap being drawn
* @param paint The paint used to draw the bitmap (may be null)
*/
- public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
+ public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
throwIfCannotDraw(bitmap);
native_drawBitmap(mNativeCanvasWrapper, bitmap.ni(), left, top,
paint != null ? paint.mNativePaint : 0, mDensity, mScreenDensity, bitmap.mDensity);
@@ -1196,7 +1264,8 @@
* to fit into
* @param paint May be null. The paint used to draw the bitmap
*/
- public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
+ public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull RectF dst,
+ @Nullable Paint paint) {
if (dst == null) {
throw new NullPointerException();
}
@@ -1227,7 +1296,8 @@
* to fit into
* @param paint May be null. The paint used to draw the bitmap
*/
- public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
+ public void drawBitmap(@NonNull Bitmap bitmap, @Nullable Rect src, @NonNull Rect dst,
+ @Nullable Paint paint) {
if (dst == null) {
throw new NullPointerException();
}
@@ -1261,8 +1331,8 @@
* and copies of pixel data.
*/
@Deprecated
- public void drawBitmap(int[] colors, int offset, int stride, float x, float y,
- int width, int height, boolean hasAlpha, Paint paint) {
+ public void drawBitmap(@NonNull int[] colors, int offset, int stride, float x, float y,
+ int width, int height, boolean hasAlpha, @Nullable Paint paint) {
// check for valid input
if (width < 0) {
throw new IllegalArgumentException("width must be >= 0");
@@ -1297,8 +1367,8 @@
* and copies of pixel data.
*/
@Deprecated
- public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
- int width, int height, boolean hasAlpha, Paint paint) {
+ public void drawBitmap(@NonNull int[] colors, int offset, int stride, int x, int y,
+ int width, int height, boolean hasAlpha, @Nullable Paint paint) {
// call through to the common float version
drawBitmap(colors, offset, stride, (float)x, (float)y, width, height,
hasAlpha, paint);
@@ -1311,7 +1381,7 @@
* @param matrix The matrix used to transform the bitmap when it is drawn
* @param paint May be null. The paint used to draw the bitmap
*/
- public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
+ public void drawBitmap(@NonNull Bitmap bitmap, @NonNull Matrix matrix, @Nullable Paint paint) {
nativeDrawBitmapMatrix(mNativeCanvasWrapper, bitmap.ni(), matrix.ni(),
paint != null ? paint.mNativePaint : 0);
}
@@ -1351,8 +1421,9 @@
* @param colorOffset Number of color elements to skip before drawing
* @param paint May be null. The paint used to draw the bitmap
*/
- public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight,
- float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint) {
+ public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
+ @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
+ @Nullable Paint paint) {
if ((meshWidth | meshHeight | vertOffset | colorOffset) < 0) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -1416,9 +1487,10 @@
* @param indexCount number of entries in the indices array (if not null).
* @param paint Specifies the shader to use if the texs array is non-null.
*/
- public void drawVertices(VertexMode mode, int vertexCount, float[] verts, int vertOffset,
- float[] texs, int texOffset, int[] colors, int colorOffset,
- short[] indices, int indexOffset, int indexCount, Paint paint) {
+ public void drawVertices(@NonNull VertexMode mode, int vertexCount, @NonNull float[] verts,
+ int vertOffset, @Nullable float[] texs, int texOffset, @Nullable int[] colors,
+ int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount,
+ @NonNull Paint paint) {
checkRange(verts.length, vertOffset, vertexCount);
if (texs != null) {
checkRange(texs.length, texOffset, vertexCount);
@@ -1443,7 +1515,8 @@
* @param y The y-coordinate of the origin of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
- public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
+ public void drawText(@NonNull char[] text, int index, int count, float x, float y,
+ @NonNull Paint paint) {
if ((index | count | (index + count) |
(text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
@@ -1461,7 +1534,7 @@
* @param y The y-coordinate of the origin of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
- public void drawText(String text, float x, float y, Paint paint) {
+ public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
paint.mNativePaint, paint.mNativeTypeface);
}
@@ -1477,7 +1550,8 @@
* @param y The y-coordinate of the origin of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
- public void drawText(String text, int start, int end, float x, float y, Paint paint) {
+ public void drawText(@NonNull String text, int start, int end, float x, float y,
+ @NonNull Paint paint) {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
@@ -1498,7 +1572,8 @@
* @param y The y-coordinate of origin for where to draw the text
* @param paint The paint used for the text (e.g. color, size, style)
*/
- public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
+ public void drawText(@NonNull CharSequence text, int start, int end, float x, float y,
+ @NonNull Paint paint) {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
native_drawText(mNativeCanvasWrapper, text.toString(), start, end, x, y,
@@ -1536,8 +1611,8 @@
* @param paint the paint
* @hide
*/
- public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
- float x, float y, int dir, Paint paint) {
+ public void drawTextRun(@NonNull char[] text, int index, int count, int contextIndex,
+ int contextCount, float x, float y, int dir, @NonNull Paint paint) {
if (text == null) {
throw new NullPointerException("text is null");
@@ -1573,8 +1648,8 @@
* @param paint the paint
* @hide
*/
- public void drawTextRun(CharSequence text, int start, int end, int contextStart, int contextEnd,
- float x, float y, int dir, Paint paint) {
+ public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
+ int contextEnd, float x, float y, int dir, @NonNull Paint paint) {
if (text == null) {
throw new NullPointerException("text is null");
@@ -1621,7 +1696,8 @@
* @param paint The paint used for the text (e.g. color, size, style)
*/
@Deprecated
- public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
+ public void drawPosText(@NonNull char[] text, int index, int count, @NonNull float[] pos,
+ @NonNull Paint paint) {
if (index < 0 || index + count > text.length || count*2 > pos.length) {
throw new IndexOutOfBoundsException();
}
@@ -1641,7 +1717,7 @@
* @param paint The paint used for the text (e.g. color, size, style)
*/
@Deprecated
- public void drawPosText(String text, float[] pos, Paint paint) {
+ public void drawPosText(@NonNull String text, @NonNull float[] pos, @NonNull Paint paint) {
if (text.length()*2 > pos.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -1661,8 +1737,8 @@
* the text
* @param paint The paint used for the text (e.g. color, size, style)
*/
- public void drawTextOnPath(char[] text, int index, int count, Path path,
- float hOffset, float vOffset, Paint paint) {
+ public void drawTextOnPath(@NonNull char[] text, int index, int count, @NonNull Path path,
+ float hOffset, float vOffset, @NonNull Paint paint) {
if (index < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
@@ -1684,7 +1760,8 @@
* the text
* @param paint The paint used for the text (e.g. color, size, style)
*/
- public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
+ public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset,
+ float vOffset, @NonNull Paint paint) {
if (text.length() > 0) {
native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset,
paint.mBidiFlags, paint.mNativePaint);
@@ -1702,7 +1779,7 @@
*
* @param picture The picture to be drawn
*/
- public void drawPicture(Picture picture) {
+ public void drawPicture(@NonNull Picture picture) {
picture.endRecording();
int restoreCount = save();
picture.draw(this);
@@ -1712,7 +1789,7 @@
/**
* Draw the picture, stretched to fit into the dst rectangle.
*/
- public void drawPicture(Picture picture, RectF dst) {
+ public void drawPicture(@NonNull Picture picture, @NonNull RectF dst) {
save();
translate(dst.left, dst.top);
if (picture.getWidth() > 0 && picture.getHeight() > 0) {
@@ -1725,7 +1802,7 @@
/**
* Draw the picture, stretched to fit into the dst rectangle.
*/
- public void drawPicture(Picture picture, Rect dst) {
+ public void drawPicture(@NonNull Picture picture, @NonNull Rect dst) {
save();
translate(dst.left, dst.top);
if (picture.getWidth() > 0 && picture.getHeight() > 0) {
@@ -1764,21 +1841,30 @@
private static native void native_setBitmap(long canvasHandle,
long bitmapHandle,
boolean copyState);
- private static native int native_saveLayer(long nativeCanvas,
- RectF bounds,
- long nativePaint,
- int layerFlags);
+ private static native boolean native_isOpaque(long canvasHandle);
+ private static native int native_getWidth(long canvasHandle);
+ private static native int native_getHeight(long canvasHandle);
+
+ private static native int native_save(long canvasHandle, int saveFlags);
private static native int native_saveLayer(long nativeCanvas, float l,
float t, float r, float b,
long nativePaint,
int layerFlags);
- private static native int native_saveLayerAlpha(long nativeCanvas,
- RectF bounds, int alpha,
- int layerFlags);
private static native int native_saveLayerAlpha(long nativeCanvas, float l,
float t, float r, float b,
int alpha, int layerFlags);
+ private static native void native_restore(long canvasHandle);
+ private static native void native_restoreToCount(long canvasHandle,
+ int saveCount);
+ private static native int native_getSaveCount(long canvasHandle);
+ private static native void native_translate(long canvasHandle,
+ float dx, float dy);
+ private static native void native_scale(long canvasHandle,
+ float sx, float sy);
+ private static native void native_rotate(long canvasHandle, float degrees);
+ private static native void native_skew(long canvasHandle,
+ float sx, float sy);
private static native void native_concat(long nativeCanvas,
long nativeMatrix);
private static native void native_setMatrix(long nativeCanvas,
@@ -1800,8 +1886,6 @@
private static native void native_getCTM(long nativeCanvas,
long nativeMatrix);
private static native boolean native_quickReject(long nativeCanvas,
- RectF rect);
- private static native boolean native_quickReject(long nativeCanvas,
long nativePath);
private static native boolean native_quickReject(long nativeCanvas,
float left, float top,
@@ -1815,11 +1899,17 @@
int mode);
private static native void native_drawPaint(long nativeCanvas,
long nativePaint);
+ private static native void native_drawPoint(long canvasHandle, float x, float y,
+ long paintHandle);
+ private static native void native_drawPoints(long canvasHandle, float[] pts,
+ int offset, int count,
+ long paintHandle);
private static native void native_drawLine(long nativeCanvas, float startX,
float startY, float stopX,
float stopY, long nativePaint);
- private static native void native_drawRect(long nativeCanvas, RectF rect,
- long nativePaint);
+ private static native void native_drawLines(long canvasHandle, float[] pts,
+ int offset, int count,
+ long paintHandle);
private static native void native_drawRect(long nativeCanvas, float left,
float top, float right,
float bottom,
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 6802b9a..11d3165 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.content.res.AssetManager;
+
import java.io.File;
/**
@@ -58,11 +60,17 @@
}
}
- public boolean addFont(File path) {
- return nAddFont(mNativePtr, path.getAbsolutePath());
+ public boolean addFont(String path) {
+ return nAddFont(mNativePtr, path);
}
- static native long nCreateFamily(String lang, int variant);
- static native void nUnrefFamily(long nativePtr);
- static native boolean nAddFont(long nativeFamily, String path);
+ public boolean addFontFromAsset(AssetManager mgr, String path) {
+ return nAddFontFromAsset(mNativePtr, mgr, path);
+ }
+
+ private static native long nCreateFamily(String lang, int variant);
+ private static native void nUnrefFamily(long nativePtr);
+ private static native boolean nAddFont(long nativeFamily, String path);
+ private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
+ String path);
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 92cfd6b..4268a24 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2131,7 +2131,7 @@
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, mBidiFlags, text, index, count, x, y,
+ native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
path.ni());
}
@@ -2153,7 +2153,7 @@
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, mBidiFlags, text, start, end, x, y,
+ native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
path.ni());
}
@@ -2174,7 +2174,7 @@
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
- nativeGetStringBounds(mNativePaint, text, start, end, mBidiFlags, bounds);
+ nativeGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
}
/**
@@ -2194,7 +2194,8 @@
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
- nativeGetCharArrayBounds(mNativePaint, text, index, count, mBidiFlags, bounds);
+ nativeGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
+ bounds);
}
@Override
@@ -2261,13 +2262,13 @@
private native int native_getTextRunCursor(long native_object, String text,
int contextStart, int contextEnd, int flags, int offset, int cursorOpt);
- private static native void native_getTextPath(long native_object, int bidiFlags,
- char[] text, int index, int count, float x, float y, long path);
- private static native void native_getTextPath(long native_object, int bidiFlags,
- String text, int start, int end, float x, float y, long path);
- private static native void nativeGetStringBounds(long nativePaint,
+ private static native void native_getTextPath(long native_object, long native_typeface,
+ int bidiFlags, char[] text, int index, int count, float x, float y, long path);
+ private static native void native_getTextPath(long native_object, long native_typeface,
+ int bidiFlags, String text, int start, int end, float x, float y, long path);
+ private static native void nativeGetStringBounds(long nativePaint, long native_typeface,
String text, int start, int end, int bidiFlags, Rect bounds);
- private static native void nativeGetCharArrayBounds(long nativePaint,
+ private static native void nativeGetCharArrayBounds(long nativePaint, long native_typeface,
char[] text, int index, int count, int bidiFlags, Rect bounds);
private static native void finalizer(long nativePaint);
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index de458af..a021165 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -53,7 +53,7 @@
public Picture(Picture src) {
this(nativeConstructor(src != null ? src.mNativePicture : 0), false);
}
-
+
/**
* To record a picture, call beginRecording() and then draw into the Canvas
* that is returned. Nothing we appear on screen, but all of the draw
@@ -67,7 +67,7 @@
mRecordingCanvas = new RecordingCanvas(this, ni);
return mRecordingCanvas;
}
-
+
/**
* Call endRecording when the picture is built. After this call, the picture
* may be drawn, but the canvas that was returned by beginRecording must not
@@ -92,16 +92,19 @@
* does not reflect (per se) the content of the picture.
*/
public native int getHeight();
-
+
/**
- * Draw this picture on the canvas. The picture may have the side effect
- * of changing the matrix and clip of the canvas.
- *
+ * Draw this picture on the canvas.
+ * <p>
+ * Prior to {@link android.os.Build.VERSION_CODES#L}, this call could
+ * have the side effect of changing the matrix and clip of the canvas
+ * if this picture had imbalanced saves/restores.
+ *
* <p>
* <strong>Note:</strong> This forces the picture to internally call
* {@link Picture#endRecording()} in order to prepare for playback.
*
- * @param canvas The picture is drawn to this canvas
+ * @param canvas The picture is drawn to this canvas
*/
public void draw(Canvas canvas) {
if (mRecordingCanvas != null) {
@@ -119,7 +122,7 @@
* <p>
* <strong>Note:</strong> a picture created from an input stream cannot be
* replayed on a hardware accelerated canvas.
- *
+ *
* @see #writeToStream(java.io.OutputStream)
* @deprecated The recommended alternative is to not use writeToStream and
* instead draw the picture into a Bitmap from which you can persist it as
@@ -167,7 +170,7 @@
final long ni() {
return mNativePicture;
}
-
+
private Picture(long nativePicture, boolean fromStream) {
if (nativePicture == 0) {
throw new RuntimeException();
@@ -187,7 +190,7 @@
private static native boolean nativeWriteToStream(long nativePicture,
OutputStream stream, byte[] storage);
private static native void nativeDestructor(long nativePicture);
-
+
private static class RecordingCanvas extends Canvas {
private final Picture mPicture;
@@ -195,7 +198,7 @@
super(nativeCanvas);
mPicture = pict;
}
-
+
@Override
public void setBitmap(Bitmap bitmap) {
throw new RuntimeException(
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 2b07c3f..cb48de2 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -178,6 +178,15 @@
* @return The new typeface.
*/
public static Typeface createFromAsset(AssetManager mgr, String path) {
+ if (sFallbackFonts != null) {
+ FontFamily fontFamily = new FontFamily();
+ if (fontFamily.addFontFromAsset(mgr, path)) {
+ FontFamily[] families = { fontFamily };
+ return createFromFamiliesWithDefault(families);
+ } else {
+ return null;
+ }
+ }
return new Typeface(nativeCreateFromAsset(mgr, path));
}
@@ -188,7 +197,7 @@
* @return The new typeface.
*/
public static Typeface createFromFile(File path) {
- return new Typeface(nativeCreateFromFile(path.getAbsolutePath()));
+ return createFromFile(path.getAbsolutePath());
}
/**
@@ -198,6 +207,15 @@
* @return The new typeface.
*/
public static Typeface createFromFile(String path) {
+ if (sFallbackFonts != null) {
+ FontFamily fontFamily = new FontFamily();
+ if (fontFamily.addFont(path)) {
+ FontFamily[] families = { fontFamily };
+ return createFromFamiliesWithDefault(families);
+ } else {
+ return null;
+ }
+ }
return new Typeface(nativeCreateFromFile(path));
}
@@ -247,7 +265,7 @@
// TODO: expand to handle attributes like lang and variant
FontFamily fontFamily = new FontFamily(family.lang, family.variant);
for (String fontFile : family.fontFiles) {
- fontFamily.addFont(new File(fontFile));
+ fontFamily.addFont(fontFile);
}
return fontFamily;
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index a37ceef..e8024f7 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -260,7 +260,7 @@
continue;
}
- if ((drawable = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme)) == null) {
+ if ((drawable = Drawable.createFromXmlInner(r, parser, attrs, theme)) == null) {
Log.w("drawable", "Bad element under <animated-rotate>: "
+ parser .getName());
}
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 42872e9..a5a074c 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -301,7 +301,7 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
final AnimationDrawable anim;
@@ -355,7 +355,7 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
return mState.addStateSet(states, dr, keyframeId);
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
new file mode 100644
index 0000000..968c0ec
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.graphics.drawable;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * AnimatedVectorDrawable can use ObjectAnimator and AnimatorSet to animate
+ * the property of the VectorDrawable.
+ *
+ * @hide
+ */
+public class AnimatedVectorDrawable extends Drawable implements Animatable {
+ private static final String LOGTAG = AnimatedVectorDrawable.class.getSimpleName();
+
+ private static final String ANIMATED_VECTOR = "animated-vector";
+ private static final String TARGET = "target";
+
+ private static final boolean DBG_ANIMATION_VECTOR_DRAWABLE = false;
+
+ private final AnimatedVectorDrawableState mAnimatedVectorState;
+
+
+ public AnimatedVectorDrawable() {
+ mAnimatedVectorState = new AnimatedVectorDrawableState(
+ new AnimatedVectorDrawableState(null));
+ }
+
+ private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res,
+ Theme theme) {
+ // TODO: Correctly handle the constant state for AVD.
+ mAnimatedVectorState = new AnimatedVectorDrawableState(state);
+ if (theme != null && canApplyTheme()) {
+ applyTheme(theme);
+ }
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return null;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mAnimatedVectorState.mVectorDrawable.draw(canvas);
+ if (isRunning()) {
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ mAnimatedVectorState.mVectorDrawable.setBounds(bounds);
+ }
+
+ @Override
+ public int getAlpha() {
+ return mAnimatedVectorState.mVectorDrawable.getAlpha();
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mAnimatedVectorState.mVectorDrawable.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mAnimatedVectorState.mVectorDrawable.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public int getOpacity() {
+ return mAnimatedVectorState.mVectorDrawable.getOpacity();
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mAnimatedVectorState.mVectorDrawable.getIntrinsicWidth();
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mAnimatedVectorState.mVectorDrawable.getIntrinsicHeight();
+ }
+
+ @Override
+ public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ final String tagName = parser.getName();
+ if (ANIMATED_VECTOR.equals(tagName)) {
+ final TypedArray a = obtainAttributes(res, theme, attrs,
+ R.styleable.AnimatedVectorDrawable);
+ int drawableRes = a.getResourceId(
+ R.styleable.AnimatedVectorDrawable_drawable, 0);
+ if (drawableRes != 0) {
+ mAnimatedVectorState.mVectorDrawable = (VectorDrawable) res.getDrawable(
+ drawableRes);
+ }
+ a.recycle();
+ } else if (TARGET.equals(tagName)) {
+ final TypedArray a = obtainAttributes(res, theme, attrs,
+ R.styleable.AnimatedVectorDrawableTarget);
+ final String target = a.getString(
+ R.styleable.AnimatedVectorDrawableTarget_name);
+
+ int id = a.getResourceId(
+ R.styleable.AnimatedVectorDrawableTarget_animation, 0);
+ if (id != 0) {
+ Animator objectAnimator = AnimatorInflater.loadAnimator(res, theme, id);
+ setupAnimatorsForTarget(target, objectAnimator);
+ }
+ a.recycle();
+ }
+ }
+
+ eventType = parser.next();
+ }
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return super.canApplyTheme() || mAnimatedVectorState != null
+ && mAnimatedVectorState.canApplyTheme();
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ super.applyTheme(t);
+
+ final VectorDrawable vectorDrawable = mAnimatedVectorState.mVectorDrawable;
+ if (vectorDrawable != null && vectorDrawable.canApplyTheme()) {
+ vectorDrawable.applyTheme(t);
+ }
+ }
+
+ private static class AnimatedVectorDrawableState extends ConstantState {
+ int mChangingConfigurations;
+ VectorDrawable mVectorDrawable;
+ ArrayList<Animator> mAnimators;
+
+ public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy) {
+ if (copy != null) {
+ mChangingConfigurations = copy.mChangingConfigurations;
+ // TODO: Make sure the constant state are handled correctly.
+ mVectorDrawable = new VectorDrawable();
+ mAnimators = new ArrayList<Animator>();
+ }
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ return new AnimatedVectorDrawable(this, null, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res) {
+ return new AnimatedVectorDrawable(this, res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new AnimatedVectorDrawable(this, res, theme);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return mChangingConfigurations;
+ }
+ }
+
+ private void setupAnimatorsForTarget(String name, Animator animator) {
+ Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name);
+ animator.setTarget(target);
+ mAnimatedVectorState.mAnimators.add(animator);
+ if (DBG_ANIMATION_VECTOR_DRAWABLE) {
+ Log.v(LOGTAG, "add animator for target " + name + " " + animator);
+ }
+ }
+
+ @Override
+ public boolean isRunning() {
+ final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
+ final int size = animators.size();
+ for (int i = 0; i < size; i++) {
+ final Animator animator = animators.get(i);
+ if (animator.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void start() {
+ final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
+ final int size = animators.size();
+ for (int i = 0; i < size; i++) {
+ final Animator animator = animators.get(i);
+ if (animator.isPaused()) {
+ animator.resume();
+ } else if (!animator.isRunning()) {
+ animator.start();
+ }
+ }
+ invalidateSelf();
+ }
+
+ @Override
+ public void stop() {
+ final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
+ final int size = animators.size();
+ for (int i = 0; i < size; i++) {
+ final Animator animator = animators.get(i);
+ animator.pause();
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index da4bc10..16548d0 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -94,7 +94,7 @@
boolean changed = super.setVisible(visible, restart);
if (visible) {
if (changed || restart) {
- setFrame(0, true, mCurFrame >= 0);
+ setFrame(0, true, restart || mCurFrame >= 0);
}
} else {
unscheduleSelf(this);
@@ -293,7 +293,7 @@
": <item> tag requires a 'drawable' attribute or child tag" +
" defining a drawable");
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
mAnimationState.addFrame(dr, duration);
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index c95ac82..ef6c085 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -706,10 +706,24 @@
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.BitmapDrawable);
updateStateFromTypedArray(a);
+ verifyState(a);
a.recycle();
}
/**
+ * Ensures all required attributes are set.
+ *
+ * @throws XmlPullParserException if any required attributes are missing
+ */
+ private void verifyState(TypedArray a) throws XmlPullParserException {
+ final BitmapState state = mBitmapState;
+ if (state.mBitmap == null) {
+ throw new XmlPullParserException(a.getPositionDescription() +
+ ": <bitmap> requires a valid src attribute");
+ }
+ }
+
+ /**
* Updates the constant state from the values in the typed array.
*/
private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
@@ -912,6 +926,7 @@
*/
private BitmapDrawable(BitmapState state, Resources res, Theme theme) {
if (theme != null && state.canApplyTheme()) {
+ // If we need to apply a theme, implicitly mutate.
mBitmapState = new BitmapState(state);
applyTheme(theme);
} else {
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 3dbd235..3ac9972 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -95,7 +95,7 @@
if (type != XmlPullParser.START_TAG) {
continue;
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index cc2a595..6a7757b 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -209,7 +209,7 @@
* stored bounds of this drawable.
*
* @see #copyBounds()
- * @see #copyBounds(android.graphics.Rect)
+ * @see #copyBounds(android.graphics.Rect)
*/
public final Rect getBounds() {
if (mBounds == ZERO_BOUNDS_RECT) {
@@ -328,8 +328,8 @@
* that want to support animated drawables.
*
* @param cb The client's Callback implementation.
- *
- * @see #getCallback()
+ *
+ * @see #getCallback()
*/
public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
@@ -338,10 +338,10 @@
/**
* Return the current {@link Callback} implementation attached to this
* Drawable.
- *
+ *
* @return A {@link Callback} instance or null if no callback was set.
- *
- * @see #setCallback(android.graphics.drawable.Drawable.Callback)
+ *
+ * @see #setCallback(android.graphics.drawable.Drawable.Callback)
*/
public Callback getCallback() {
if (mCallback != null) {
@@ -349,15 +349,15 @@
}
return null;
}
-
+
/**
* Use the current {@link Callback} implementation to have this Drawable
* redrawn. Does nothing if there is no Callback attached to the
* Drawable.
*
* @see Callback#invalidateDrawable
- * @see #getCallback()
- * @see #setCallback(android.graphics.drawable.Drawable.Callback)
+ * @see #getCallback()
+ * @see #setCallback(android.graphics.drawable.Drawable.Callback)
*/
public void invalidateSelf() {
final Callback callback = getCallback();
@@ -892,13 +892,9 @@
* Create a drawable from an inputstream
*/
public static Drawable createFromStream(InputStream is, String srcName) {
- return createFromStreamThemed(is, srcName, null);
- }
-
- public static Drawable createFromStreamThemed(InputStream is, String srcName, Theme theme) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
try {
- return createFromResourceStreamThemed(null, null, is, srcName, theme);
+ return createFromResourceStream(null, null, is, srcName);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
@@ -910,14 +906,9 @@
*/
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName) {
- return createFromResourceStreamThemed(res, value, is, srcName, null);
- }
-
- public static Drawable createFromResourceStreamThemed(Resources res, TypedValue value,
- InputStream is, String srcName, Theme theme) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
try {
- return createFromResourceStreamThemed(res, value, is, srcName, null, theme);
+ return createFromResourceStream(res, value, is, srcName, null);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
@@ -929,11 +920,6 @@
*/
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName, BitmapFactory.Options opts) {
- return createFromResourceStreamThemed(res, value, is, srcName, opts, null);
- }
-
- public static Drawable createFromResourceStreamThemed(Resources res, TypedValue value,
- InputStream is, String srcName, BitmapFactory.Options opts, Theme theme) {
if (is == null) {
return null;
}
@@ -945,7 +931,7 @@
Rects only to drop them on the floor.
*/
Rect pad = new Rect();
-
+
// Special stuff for compatibility mode: if the target density is not
// the same as the display density, but the resource -is- the same as
// the display density, then don't scale it down to the target density.
@@ -981,15 +967,15 @@
*/
public static Drawable createFromXml(Resources r, XmlPullParser parser)
throws XmlPullParserException, IOException {
- return createFromXmlThemed(r, parser, null);
+ return createFromXml(r, parser, null);
}
/**
- * Create a themed drawable from an XML document. For more information on
- * how to create resources in XML, see
+ * Create a drawable from an XML document using an optional {@link Theme}.
+ * For more information on how to create resources in XML, see
* <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
- public static Drawable createFromXmlThemed(Resources r, XmlPullParser parser, Theme theme)
+ public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)
throws XmlPullParserException, IOException {
AttributeSet attrs = Xml.asAttributeSet(parser);
@@ -1003,7 +989,7 @@
throw new XmlPullParserException("No start tag found");
}
- Drawable drawable = createFromXmlInnerThemed(r, parser, attrs, theme);
+ Drawable drawable = createFromXmlInner(r, parser, attrs, theme);
if (drawable == null) {
throw new RuntimeException("Unknown initial tag: " + parser.getName());
@@ -1019,16 +1005,17 @@
*/
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
- return createFromXmlInnerThemed(r, parser, attrs, null);
+ return createFromXmlInner(r, parser, attrs, null);
}
/**
- * Create a themed drawable from inside an XML document. Called on a parser
- * positioned at a tag in an XML document, tries to create a Drawable from
- * that tag. Returns null if the tag is not a valid drawable.
+ * Create a drawable from inside an XML document using an optional
+ * {@link Theme}. Called on a parser positioned at a tag in an XML
+ * document, tries to create a Drawable from that tag. Returns {@code null}
+ * if the tag is not a valid drawable.
*/
- public static Drawable createFromXmlInnerThemed(Resources r, XmlPullParser parser,
- AttributeSet attrs, Theme theme) throws XmlPullParserException, IOException {
+ public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,
+ Theme theme) throws XmlPullParserException, IOException {
final Drawable drawable;
final String name = parser.getName();
@@ -1036,6 +1023,9 @@
drawable = new StateListDrawable();
} else if (name.equals("animated-selector")) {
drawable = new AnimatedStateListDrawable();
+ } else if (name.equals("material-progress")) {
+ // TODO: Replace this with something less ridiculous.
+ drawable = new MaterialProgressDrawable();
} else if (name.equals("level-list")) {
drawable = new LevelListDrawable();
} else if (name.equals("layer-list")) {
@@ -1050,6 +1040,8 @@
drawable = new GradientDrawable();
} else if (name.equals("vector")) {
drawable = new VectorDrawable();
+ } else if (name.equals("animated-vector")) {
+ drawable = new AnimatedVectorDrawable();
} else if (name.equals("scale")) {
drawable = new ScaleDrawable();
} else if (name.equals("clip")) {
@@ -1057,7 +1049,7 @@
} else if (name.equals("rotate")) {
drawable = new RotateDrawable();
} else if (name.equals("animated-rotate")) {
- drawable = new AnimatedRotateDrawable();
+ drawable = new AnimatedRotateDrawable();
} else if (name.equals("animation-list")) {
drawable = new AnimationDrawable();
} else if (name.equals("inset")) {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 1512da5..005b8ef 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -1203,8 +1203,11 @@
st.mAttrStroke = a.extractThemeAttrs();
+ // We have an explicit stroke defined, so the default stroke width
+ // must be at least 0 or the current stroke width.
+ final int defaultStrokeWidth = Math.max(0, st.mStrokeWidth);
final int width = a.getDimensionPixelSize(
- R.styleable.GradientDrawableStroke_width, st.mStrokeWidth);
+ R.styleable.GradientDrawableStroke_width, defaultStrokeWidth);
final float dashWidth = a.getDimension(
R.styleable.GradientDrawableStroke_dashWidth, st.mStrokeDashWidth);
@@ -1406,10 +1409,13 @@
outline.setOval(bounds);
return true;
case LINE:
- float halfStrokeWidth = mStrokePaint.getStrokeWidth() * 0.5f;
- float centerY = bounds.centerY();
- int top = (int) Math.floor(centerY - halfStrokeWidth);
- int bottom = (int) Math.ceil(centerY + halfStrokeWidth);
+ // Hairlines (0-width stroke) must have a non-empty outline for
+ // shadows to draw correctly, so we'll use a very small width.
+ final float halfStrokeWidth = mStrokePaint == null ?
+ 0.0001f : mStrokePaint.getStrokeWidth() * 0.5f;
+ final float centerY = bounds.centerY();
+ final int top = (int) Math.floor(centerY - halfStrokeWidth);
+ final int bottom = (int) Math.ceil(centerY + halfStrokeWidth);
outline.setRect(bounds.left, top, bounds.right, bottom);
return true;
@@ -1656,9 +1662,12 @@
* @param theme Theme to apply to the drawable
*/
private GradientDrawable(GradientState state, Theme theme) {
- mGradientState = new GradientState(state);
if (theme != null && state.canApplyTheme()) {
+ // If we need to apply a theme, implicitly mutate.
+ mGradientState = new GradientState(state);
applyTheme(theme);
+ } else {
+ mGradientState = state;
}
initializeWithState(state);
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 2fa5929..9e0ab86 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -111,7 +111,7 @@
+ ": <inset> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 75cb0a00..27f0a9d 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -211,7 +211,7 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
addLayer(dr, themeAttrs, id, left, top, right, bottom);
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index 9f6c0ad..7271b0e 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -134,7 +134,7 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
mLevelListState.addLevel(low, high, dr);
diff --git a/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java b/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java
new file mode 100644
index 0000000..9e56f67
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/MaterialProgressDrawable.java
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Paint.Cap;
+import android.graphics.Paint.Style;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Fancy progress indicator for Material theme.
+ *
+ * TODO: Replace this class with something less ridiculous.
+ */
+class MaterialProgressDrawable extends Drawable implements Animatable {
+ private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+ private static final TimeInterpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator();
+ private static final TimeInterpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator();
+
+ /** The duration of a single progress spin in milliseconds. */
+ private static final int ANIMATION_DURATION = 1000 * 80 / 60;
+
+ /** The number of points in the progress "star". */
+ private static final int NUM_POINTS = 5;
+
+ /** The list of animators operating on this drawable. */
+ private final ArrayList<Animator> mAnimators = new ArrayList<Animator>();
+
+ /** The indicator ring, used to manage animation state. */
+ private final Ring mRing;
+
+ private MaterialProgressState mState;
+
+ /** Canvas rotation in degrees. */
+ private float mRotation;
+
+ private boolean mMutated;
+
+ public MaterialProgressDrawable() {
+ this(new MaterialProgressState(null), null);
+ }
+
+ private MaterialProgressDrawable(MaterialProgressState state, Theme theme) {
+ mState = state;
+ if (theme != null && state.canApplyTheme()) {
+ applyTheme(theme);
+ }
+
+ mRing = new Ring(mCallback);
+ mMutated = false;
+
+ initializeFromState();
+ setupAnimators();
+ }
+
+ private void initializeFromState() {
+ final MaterialProgressState state = mState;
+
+ final Ring ring = mRing;
+ ring.setStrokeWidth(state.mStrokeWidth);
+
+ final int color = state.mColor.getColorForState(getState(), Color.TRANSPARENT);
+ ring.setColor(color);
+
+ final float minEdge = Math.min(state.mWidth, state.mHeight);
+ if (state.mInnerRadius <= 0 || minEdge < 0) {
+ ring.setInsets((int) Math.ceil(state.mStrokeWidth / 2.0f));
+ } else {
+ float insets = minEdge / 2.0f - state.mInnerRadius;
+ ring.setInsets(insets);
+ }
+ }
+
+ @Override
+ public Drawable mutate() {
+ if (!mMutated && super.mutate() == this) {
+ mState = new MaterialProgressState(mState);
+ mMutated = true;
+ }
+ return this;
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ boolean changed = super.onStateChange(state);
+
+ final int color = mState.mColor.getColorForState(state, Color.TRANSPARENT);
+ if (color != mRing.getColor()) {
+ mRing.setColor(color);
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ @Override
+ public boolean isStateful() {
+ return super.isStateful() || mState.mColor.isStateful();
+ }
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.MaterialProgressDrawable);
+ super.inflateWithAttributes(r, parser, a, R.styleable.MaterialProgressDrawable_visible);
+ updateStateFromTypedArray(a);
+ a.recycle();
+
+ initializeFromState();
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ final TypedArray a = t.resolveAttributes(mState.mThemeAttrs,
+ R.styleable.MaterialProgressDrawable);
+ updateStateFromTypedArray(a);
+ a.recycle();
+ }
+
+ private void updateStateFromTypedArray(TypedArray a) {
+ final MaterialProgressState state = mState;
+ state.mThemeAttrs = a.extractThemeAttrs();
+ state.mWidth = a.getDimensionPixelSize(
+ R.styleable.MaterialProgressDrawable_width, state.mWidth);
+ state.mHeight = a.getDimensionPixelSize(
+ R.styleable.MaterialProgressDrawable_height, state.mHeight);
+ state.mInnerRadius = a.getDimension(
+ R.styleable.MaterialProgressDrawable_innerRadius, state.mInnerRadius);
+ state.mStrokeWidth = a.getDimension(
+ R.styleable.MaterialProgressDrawable_thickness, state.mStrokeWidth);
+
+ if (a.hasValue(R.styleable.MaterialProgressDrawable_color)) {
+ state.mColor = a.getColorStateList(R.styleable.MaterialProgressDrawable_color);
+ }
+ }
+
+ @Override
+ public boolean setVisible(boolean visible, boolean restart) {
+ boolean changed = super.setVisible(visible, restart);
+ if (visible) {
+ if (changed || restart) {
+ start();
+ }
+ } else {
+ stop();
+ }
+ return changed;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mState.mHeight;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mState.mWidth;
+ }
+
+ @Override
+ public void draw(Canvas c) {
+ final Rect bounds = getBounds();
+ final int saveCount = c.save();
+ c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
+ mRing.draw(c, bounds);
+ c.restoreToCount(saveCount);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mRing.setAlpha(alpha);
+ }
+
+ @Override
+ public int getAlpha() {
+ return mRing.getAlpha();
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mRing.setColorFilter(colorFilter);
+ }
+
+ @Override
+ public ColorFilter getColorFilter() {
+ return mRing.getColorFilter();
+ }
+
+ private void setRotation(float rotation) {
+ mRotation = rotation;
+ invalidateSelf();
+ }
+
+ private float getRotation() {
+ return mRotation;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public boolean isRunning() {
+ final ArrayList<Animator> animators = mAnimators;
+ final int N = animators.size();
+ for (int i = 0; i < N; i++) {
+ final Animator animator = animators.get(i);
+ if (animator.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void start() {
+ final ArrayList<Animator> animators = mAnimators;
+ final int N = animators.size();
+ for (int i = 0; i < N; i++) {
+ final Animator animator = animators.get(i);
+ if (animator.isPaused()) {
+ animator.resume();
+ } else if (!animator.isRunning()){
+ animator.start();
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+ final ArrayList<Animator> animators = mAnimators;
+ final int N = animators.size();
+ for (int i = 0; i < N; i++) {
+ final Animator animator = animators.get(i);
+ animator.pause();
+ }
+ }
+
+ private void setupAnimators() {
+ final Ring ring = mRing;
+
+ final ObjectAnimator endTrim = ObjectAnimator.ofFloat(ring, "endTrim", 0, 0.75f);
+ endTrim.setDuration(ANIMATION_DURATION);
+ endTrim.setInterpolator(START_CURVE_INTERPOLATOR);
+ endTrim.setRepeatCount(ObjectAnimator.INFINITE);
+ endTrim.setRepeatMode(ObjectAnimator.RESTART);
+
+ final ObjectAnimator startTrim = ObjectAnimator.ofFloat(ring, "startTrim", 0.0f, 0.75f);
+ startTrim.setDuration(ANIMATION_DURATION);
+ startTrim.setInterpolator(END_CURVE_INTERPOLATOR);
+ startTrim.setRepeatCount(ObjectAnimator.INFINITE);
+ startTrim.setRepeatMode(ObjectAnimator.RESTART);
+
+ final ObjectAnimator rotation = ObjectAnimator.ofFloat(ring, "rotation", 0.0f, 0.25f);
+ rotation.setDuration(ANIMATION_DURATION);
+ rotation.setInterpolator(LINEAR_INTERPOLATOR);
+ rotation.setRepeatCount(ObjectAnimator.INFINITE);
+ rotation.setRepeatMode(ObjectAnimator.RESTART);
+
+ final ObjectAnimator groupRotation = ObjectAnimator.ofFloat(this, "rotation", 0.0f, 360.0f);
+ groupRotation.setDuration(NUM_POINTS * ANIMATION_DURATION);
+ groupRotation.setInterpolator(LINEAR_INTERPOLATOR);
+ groupRotation.setRepeatCount(ObjectAnimator.INFINITE);
+ groupRotation.setRepeatMode(ObjectAnimator.RESTART);
+
+ mAnimators.add(endTrim);
+ mAnimators.add(startTrim);
+ mAnimators.add(rotation);
+ mAnimators.add(groupRotation);
+ }
+
+ private final Callback mCallback = new Callback() {
+ @Override
+ public void invalidateDrawable(Drawable d) {
+ invalidateSelf();
+ }
+
+ @Override
+ public void scheduleDrawable(Drawable d, Runnable what, long when) {
+ scheduleSelf(what, when);
+ }
+
+ @Override
+ public void unscheduleDrawable(Drawable d, Runnable what) {
+ unscheduleSelf(what);
+ }
+ };
+
+ private static class MaterialProgressState extends ConstantState {
+ private int[] mThemeAttrs = null;
+ private float mStrokeWidth = 5.0f;
+ private float mInnerRadius = -1.0f;
+ private int mWidth = -1;
+ private int mHeight = -1;
+ private ColorStateList mColor = ColorStateList.valueOf(Color.TRANSPARENT);
+
+ public MaterialProgressState(MaterialProgressState orig) {
+ if (orig != null) {
+ mThemeAttrs = orig.mThemeAttrs;
+ mStrokeWidth = orig.mStrokeWidth;
+ mInnerRadius = orig.mInnerRadius;
+ mWidth = orig.mWidth;
+ mHeight = orig.mHeight;
+ mColor = orig.mColor;
+ }
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ return newDrawable(null, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res) {
+ return newDrawable(res, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res, Theme theme) {
+ return new MaterialProgressDrawable(this, theme);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+ }
+
+ private static class Ring {
+ private final RectF mTempBounds = new RectF();
+ private final Paint mPaint = new Paint();
+
+ private final Callback mCallback;
+
+ private float mStartTrim = 0.0f;
+ private float mEndTrim = 0.0f;
+ private float mRotation = 0.0f;
+ private float mStrokeWidth = 5.0f;
+ private float mStrokeInset = 2.5f;
+
+ private int mAlpha = 0xFF;
+ private int mColor = Color.BLACK;
+
+ public Ring(Callback callback) {
+ mCallback = callback;
+
+ mPaint.setStrokeCap(Cap.ROUND);
+ mPaint.setAntiAlias(true);
+ mPaint.setStyle(Style.STROKE);
+ }
+
+ public void draw(Canvas c, Rect bounds) {
+ final RectF arcBounds = mTempBounds;
+ arcBounds.set(bounds);
+ arcBounds.inset(mStrokeInset, mStrokeInset);
+
+ final float startAngle = (mStartTrim + mRotation) * 360;
+ final float endAngle = (mEndTrim + mRotation) * 360;
+ float sweepAngle = endAngle - startAngle;
+
+ // Ensure the sweep angle isn't too small to draw.
+ final float diameter = Math.min(arcBounds.width(), arcBounds.height());
+ final float minAngle = (float) (360.0 / (diameter * Math.PI));
+ if (sweepAngle < minAngle && sweepAngle > -minAngle) {
+ sweepAngle = Math.signum(sweepAngle) * minAngle;
+ }
+
+ c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
+ }
+
+ public void setColorFilter(ColorFilter filter) {
+ mPaint.setColorFilter(filter);
+ invalidateSelf();
+ }
+
+ public ColorFilter getColorFilter() {
+ return mPaint.getColorFilter();
+ }
+
+ public void setAlpha(int alpha) {
+ mAlpha = alpha;
+ mPaint.setColor(mColor & 0xFFFFFF | alpha << 24);
+ invalidateSelf();
+ }
+
+ public int getAlpha() {
+ return mAlpha;
+ }
+
+ public void setColor(int color) {
+ mColor = color;
+ mPaint.setColor(color & 0xFFFFFF | mAlpha << 24);
+ invalidateSelf();
+ }
+
+ public int getColor() {
+ return mColor;
+ }
+
+ public void setStrokeWidth(float strokeWidth) {
+ mStrokeWidth = strokeWidth;
+ mPaint.setStrokeWidth(strokeWidth);
+ invalidateSelf();
+ }
+
+ @SuppressWarnings("unused")
+ public float getStrokeWidth() {
+ return mStrokeWidth;
+ }
+
+ @SuppressWarnings("unused")
+ public void setStartTrim(float startTrim) {
+ mStartTrim = startTrim;
+ invalidateSelf();
+ }
+
+ @SuppressWarnings("unused")
+ public float getStartTrim() {
+ return mStartTrim;
+ }
+
+ @SuppressWarnings("unused")
+ public void setEndTrim(float endTrim) {
+ mEndTrim = endTrim;
+ invalidateSelf();
+ }
+
+ @SuppressWarnings("unused")
+ public float getEndTrim() {
+ return mEndTrim;
+ }
+
+ @SuppressWarnings("unused")
+ public void setRotation(float rotation) {
+ mRotation = rotation;
+ invalidateSelf();
+ }
+
+ @SuppressWarnings("unused")
+ public float getRotation() {
+ return mRotation;
+ }
+
+ public void setInsets(float insets) {
+ mStrokeInset = insets;
+ }
+
+ @SuppressWarnings("unused")
+ public float getInsets() {
+ return mStrokeInset;
+ }
+
+ private void invalidateSelf() {
+ mCallback.invalidateDrawable(null);
+ }
+ }
+
+ /**
+ * Squishes the interpolation curve into the second half of the animation.
+ */
+ private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator {
+ @Override
+ public float getInterpolation(float input) {
+ return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f));
+ }
+ }
+
+ /**
+ * Squishes the interpolation curve into the first half of the animation.
+ */
+ private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator {
+ @Override
+ public float getInterpolation(float input) {
+ return super.getInterpolation(Math.min(1, input * 2.0f));
+ }
+ }
+}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 0a07332..fea68ee 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -23,10 +23,12 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.NinePatch;
import android.graphics.Paint;
+import android.graphics.Paint.Style;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
@@ -236,7 +238,6 @@
final boolean needsMirroring = needsMirroring();
if (needsMirroring) {
- canvas.save();
// Mirror the 9patch
canvas.translate(bounds.right - bounds.left, 0);
canvas.scale(-1.0f, 1.0f);
@@ -253,10 +254,6 @@
mNinePatch.draw(canvas, bounds, mPaint);
- if (needsMirroring) {
- canvas.restore();
- }
-
if (clearColorFilter) {
mPaint.setColorFilter(null);
}
@@ -662,6 +659,7 @@
*/
private NinePatchDrawable(NinePatchState state, Resources res, Theme theme) {
if (theme != null && state.canApplyTheme()) {
+ // If we need to apply a theme, implicitly mutate.
mNinePatchState = new NinePatchState(state);
applyTheme(theme);
} else {
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index ada741b..0d70e75 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -28,22 +28,26 @@
import android.util.MathUtils;
import android.view.HardwareCanvas;
import android.view.RenderNodeAnimator;
+import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import java.util.ArrayList;
/**
- * Draws a Quantum Paper ripple.
+ * Draws a Material ripple.
*/
class Ripple {
private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+ private static final TimeInterpolator DECEL_INTERPOLATOR = new DecelerateInterpolator(4);
private static final float GLOBAL_SPEED = 1.0f;
- private static final float WAVE_TOUCH_DOWN_ACCELERATION = 512.0f * GLOBAL_SPEED;
- private static final float WAVE_TOUCH_UP_ACCELERATION = 1024.0f * GLOBAL_SPEED;
- private static final float WAVE_OPACITY_DECAY_VELOCITY = 1.6f / GLOBAL_SPEED;
+ private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024.0f * GLOBAL_SPEED;
+ private static final float WAVE_TOUCH_UP_ACCELERATION = 3096.0f * GLOBAL_SPEED;
+ private static final float WAVE_OPACITY_DECAY_VELOCITY = 1.9f / GLOBAL_SPEED;
private static final float WAVE_OUTER_OPACITY_VELOCITY = 1.2f * GLOBAL_SPEED;
+ private static final long RIPPLE_ENTER_DELAY = 100;
+
// Hardware animators.
private final ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<>();
private final ArrayList<RenderNodeAnimator> mPendingAnimations = new ArrayList<>();
@@ -64,6 +68,8 @@
private float mStartingX;
private float mStartingY;
+ private float mClampedStartingX;
+ private float mClampedStartingY;
// Hardware rendering properties.
private CanvasProperty<Paint> mPropPaint;
@@ -108,6 +114,7 @@
public Ripple(RippleDrawable owner, Rect bounds, float startingX, float startingY) {
mOwner = owner;
mBounds = bounds;
+
mStartingX = startingX;
mStartingY = startingY;
}
@@ -127,6 +134,23 @@
mOuterX = 0;
mOuterY = 0;
mDensity = density;
+
+ clampStartingPosition();
+ }
+
+ private void clampStartingPosition() {
+ final float dX = mStartingX - mBounds.exactCenterX();
+ final float dY = mStartingY - mBounds.exactCenterY();
+ final float r = mOuterRadius;
+ if (dX * dX + dY * dY > r * r) {
+ // Point is outside the circle, clamp to the circumference.
+ final double angle = Math.atan2(dY, dX);
+ mClampedStartingX = (float) (Math.cos(angle) * r);
+ mClampedStartingY = (float) (Math.sin(angle) * r);
+ } else {
+ mClampedStartingX = mStartingX;
+ mClampedStartingY = mStartingY;
+ }
}
public void onHotspotBoundsChanged() {
@@ -134,6 +158,8 @@
final float halfWidth = mBounds.width() / 2.0f;
final float halfHeight = mBounds.height() / 2.0f;
mOuterRadius = (float) Math.sqrt(halfWidth * halfWidth + halfHeight * halfHeight);
+
+ clampStartingPosition();
}
}
@@ -232,7 +258,7 @@
// Cache the paint alpha so we can restore it later.
final int paintAlpha = p.getAlpha();
- final int outerAlpha = (int) (255 * mOuterOpacity + 0.5f);
+ final int outerAlpha = (int) (paintAlpha * mOuterOpacity + 0.5f);
if (outerAlpha > 0 && mOuterRadius > 0) {
p.setAlpha(outerAlpha);
p.setStyle(Style.FILL);
@@ -240,11 +266,13 @@
hasContent = true;
}
- final int alpha = (int) (255 * mOpacity + 0.5f);
+ final int alpha = (int) (paintAlpha * mOpacity + 0.5f);
final float radius = MathUtils.lerp(0, mOuterRadius, mTweenRadius);
if (alpha > 0 && radius > 0) {
- final float x = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
- final float y = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
+ final float x = MathUtils.lerp(
+ mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
+ final float y = MathUtils.lerp(
+ mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
p.setAlpha(alpha);
p.setStyle(Style.FILL);
c.drawCircle(x, y, radius, p);
@@ -273,6 +301,8 @@
public void move(float x, float y) {
mStartingX = x;
mStartingY = y;
+
+ clampStartingPosition();
}
/**
@@ -287,14 +317,19 @@
radius.setAutoCancel(true);
radius.setDuration(radiusDuration);
radius.setInterpolator(LINEAR_INTERPOLATOR);
+ radius.setStartDelay(RIPPLE_ENTER_DELAY);
final ObjectAnimator cX = ObjectAnimator.ofFloat(this, "xGravity", 1);
cX.setAutoCancel(true);
cX.setDuration(radiusDuration);
+ cX.setInterpolator(LINEAR_INTERPOLATOR);
+ cX.setStartDelay(RIPPLE_ENTER_DELAY);
final ObjectAnimator cY = ObjectAnimator.ofFloat(this, "yGravity", 1);
cY.setAutoCancel(true);
cY.setDuration(radiusDuration);
+ cY.setInterpolator(LINEAR_INTERPOLATOR);
+ cY.setStartDelay(RIPPLE_ENTER_DELAY);
final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerOpacity", 0, 1);
outer.setAutoCancel(true);
@@ -352,8 +387,10 @@
int inflectionOpacity) {
mPendingAnimations.clear();
- final float startX = MathUtils.lerp(mStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
- final float startY = MathUtils.lerp(mStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
+ final float startX = MathUtils.lerp(
+ mClampedStartingX - mBounds.exactCenterX(), mOuterX, mTweenX);
+ final float startY = MathUtils.lerp(
+ mClampedStartingY - mBounds.exactCenterY(), mOuterY, mTweenY);
final Paint outerPaint = new Paint();
outerPaint.setAntiAlias(true);
outerPaint.setColor(mColor);
@@ -377,15 +414,15 @@
final RenderNodeAnimator radiusAnim = new RenderNodeAnimator(mPropRadius, mOuterRadius);
radiusAnim.setDuration(radiusDuration);
- radiusAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ radiusAnim.setInterpolator(DECEL_INTERPOLATOR);
final RenderNodeAnimator xAnim = new RenderNodeAnimator(mPropX, mOuterX);
xAnim.setDuration(radiusDuration);
- xAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ xAnim.setInterpolator(DECEL_INTERPOLATOR);
final RenderNodeAnimator yAnim = new RenderNodeAnimator(mPropY, mOuterY);
yAnim.setDuration(radiusDuration);
- yAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ yAnim.setInterpolator(DECEL_INTERPOLATOR);
final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPropPaint,
RenderNodeAnimator.PAINT_ALPHA, 0);
@@ -408,6 +445,7 @@
outerFadeOutAnim.setDuration(outerDuration);
outerFadeOutAnim.setInterpolator(LINEAR_INTERPOLATOR);
outerFadeOutAnim.setStartDelay(outerInflection);
+ outerFadeOutAnim.setStartValue(inflectionOpacity);
outerFadeOutAnim.addListener(mAnimationListener);
mPendingAnimations.add(outerFadeOutAnim);
@@ -438,17 +476,17 @@
final ObjectAnimator radiusAnim = ObjectAnimator.ofFloat(this, "radiusGravity", 1);
radiusAnim.setAutoCancel(true);
radiusAnim.setDuration(radiusDuration);
- radiusAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ radiusAnim.setInterpolator(DECEL_INTERPOLATOR);
final ObjectAnimator xAnim = ObjectAnimator.ofFloat(this, "xGravity", 1);
xAnim.setAutoCancel(true);
xAnim.setDuration(radiusDuration);
- xAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ xAnim.setInterpolator(DECEL_INTERPOLATOR);
final ObjectAnimator yAnim = ObjectAnimator.ofFloat(this, "yGravity", 1);
yAnim.setAutoCancel(true);
yAnim.setDuration(radiusDuration);
- yAnim.setInterpolator(LINEAR_INTERPOLATOR);
+ yAnim.setInterpolator(DECEL_INTERPOLATOR);
final ObjectAnimator opacityAnim = ObjectAnimator.ofFloat(this, "opacity", 0);
opacityAnim.setAutoCancel(true);
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 9d7a8b6..0512ecc 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -48,22 +48,41 @@
* A touch feedback drawable may contain multiple child layers, including a
* special mask layer that is not drawn to the screen. A single layer may be set
* as the mask by specifying its android:id value as {@link android.R.id#mask}.
+ * <pre>
+ * <code><!-- A red ripple masked against an opaque rectangle. --/>
+ * <ripple android:color="#ffff0000">
+ * <item android:id="@android:id/mask"
+ * android:drawable="#ffffffff" />
+ * <ripple /></code>
+ * </pre>
* <p>
* If a mask layer is set, the ripple effect will be masked against that layer
- * before it is blended onto the composite of the remaining child layers.
+ * before it is drawn over the composite of the remaining child layers.
* <p>
- * If no mask layer is set, the ripple effect is simply blended onto the
- * composite of the child layers using the specified
- * {@link android.R.styleable#RippleDrawable_tintMode}.
+ * If no mask layer is set, the ripple effect is masked against the composite
+ * of the child layers.
+ * <pre>
+ * <code><!-- A blue ripple drawn atop a green rectangle. --/>
+ * <ripple android:color="#ff00ff00">
+ * <item android:drawable="#ff0000ff" />
+ * <ripple />
+ *
+ * <!-- A red ripple drawn atop a drawable resource. --/>
+ * <ripple android:color="#ff00ff00">
+ * <item android:drawable="@drawable/my_drawable" />
+ * <ripple /></code>
+ * </pre>
* <p>
* If no child layers or mask is specified and the ripple is set as a View
- * background, the ripple will be blended onto the first available parent
- * background within the View's hierarchy using the specified
- * {@link android.R.styleable#RippleDrawable_tintMode}. In this case, the
- * drawing region may extend outside of the Drawable bounds.
+ * background, the ripple will be drawn atop the first available parent
+ * background within the View's hierarchy. In this case, the drawing region
+ * may extend outside of the Drawable bounds.
+ * <pre>
+ * <code><!-- An unbounded green ripple. --/>
+ * <ripple android:color="#ff0000ff" /></code>
+ * </pre>
*
- * @attr ref android.R.styleable#DrawableStates_state_focused
- * @attr ref android.R.styleable#DrawableStates_state_pressed
+ * @attr ref android.R.styleable#RippleDrawable_color
*/
public class RippleDrawable extends LayerDrawable {
private static final String LOG_TAG = RippleDrawable.class.getSimpleName();
@@ -95,6 +114,9 @@
private final RippleState mState;
+ /** The masking layer, e.g. the layer with id R.id.mask. */
+ private Drawable mMask;
+
/** The current hotspot. May be actively animating or pending entry. */
private Ripple mHotspot;
@@ -169,20 +191,24 @@
protected boolean onStateChange(int[] stateSet) {
super.onStateChange(stateSet);
+ // TODO: This would make more sense in a StateListDrawable.
boolean active = false;
+ boolean enabled = false;
final int N = stateSet.length;
for (int i = 0; i < N; i++) {
+ if (stateSet[i] == R.attr.state_enabled) {
+ enabled = true;
+ }
if (stateSet[i] == R.attr.state_focused
|| stateSet[i] == R.attr.state_pressed) {
active = true;
- break;
}
}
- setActive(active);
+ setActive(active && enabled);
// Update the paint color. Only applicable when animated in software.
- if (mRipplePaint != null && mState.mTint != null) {
- final ColorStateList stateList = mState.mTint;
+ if (mRipplePaint != null && mState.mColor != null) {
+ final ColorStateList stateList = mState.mColor;
final int newColor = stateList.getColorForState(stateSet, 0);
final int oldColor = mRipplePaint.getColor();
if (oldColor != newColor) {
@@ -241,10 +267,8 @@
return true;
}
- @Override
- public void setTint(ColorStateList tint, Mode tintMode) {
- mState.mTint = tint;
- mState.setTintMode(tintMode);
+ public void setColor(ColorStateList color) {
+ mState.mColor = color;
invalidateSelf();
}
@@ -261,21 +285,14 @@
super.inflate(r, parser, attrs, theme);
setTargetDensity(r.getDisplayMetrics());
-
- // Find the mask
- final int N = getNumberOfLayers();
- for (int i = 0; i < N; i++) {
- if (mLayerState.mChildren[i].mId == R.id.mask) {
- mState.mMask = mLayerState.mChildren[i].mDrawable;
- }
- }
+ initializeFromState();
}
@Override
public boolean setDrawableByLayerId(int id, Drawable drawable) {
if (super.setDrawableByLayerId(id, drawable)) {
if (id == R.id.mask) {
- mState.mMask = drawable;
+ mMask = drawable;
}
return true;
@@ -312,22 +329,15 @@
// Extract the theme attributes, if any.
state.mTouchThemeAttrs = a.extractThemeAttrs();
- final ColorStateList tint = a.getColorStateList(R.styleable.RippleDrawable_tint);
- if (tint != null) {
- mState.mTint = tint;
+ final ColorStateList color = a.getColorStateList(R.styleable.RippleDrawable_color);
+ if (color != null) {
+ mState.mColor = color;
}
- final int tintMode = a.getInt(R.styleable.RippleDrawable_tintMode, -1);
- if (tintMode != -1) {
- mState.setTintMode(Drawable.parseTintMode(tintMode, Mode.SRC_ATOP));
- }
-
- mState.mPinned = a.getBoolean(R.styleable.RippleDrawable_pinned, mState.mPinned);
-
// If we're not waiting on a theme, verify required attributes.
- if (state.mTouchThemeAttrs == null && mState.mTint == null) {
+ if (state.mTouchThemeAttrs == null && mState.mColor == null) {
throw new XmlPullParserException(a.getPositionDescription() +
- ": <ripple> requires a valid tint attribute");
+ ": <ripple> requires a valid color attribute");
}
}
@@ -361,6 +371,8 @@
} finally {
a.recycle();
}
+
+ initializeFromState();
}
@Override
@@ -370,11 +382,6 @@
@Override
public void setHotspot(float x, float y) {
- if (mState.mPinned && !circleContains(mHotspotBounds, x, y)) {
- x = mHotspotBounds.exactCenterX();
- y = mHotspotBounds.exactCenterY();
- }
-
if (mHotspot == null) {
mHotspot = new Ripple(this, mHotspotBounds, x, y);
@@ -386,18 +393,6 @@
}
}
- private boolean circleContains(Rect bounds, float x, float y) {
- final float pX = bounds.exactCenterX() - x;
- final float pY = bounds.exactCenterY() - y;
- final double pointRadius = Math.sqrt(pX * pX + pY * pY);
-
- final float bX = bounds.width() / 2.0f;
- final float bY = bounds.height() / 2.0f;
- final double boundsRadius = Math.sqrt(bX * bX + bY * bY);
-
- return pointRadius < boundsRadius;
- }
-
/**
* Creates an active hotspot at the specified location.
*/
@@ -415,7 +410,7 @@
mHotspot = new Ripple(this, mHotspotBounds, x, y);
}
- final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+ final int color = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
mHotspot.setup(mState.mMaxRadius, color, mDensity);
mHotspot.enter();
@@ -441,8 +436,11 @@
final int count = mAnimatingRipplesCount;
final Ripple[] ripples = mAnimatingRipples;
for (int i = 0; i < count; i++) {
- ripples[i].cancel();
+ // Calling cancel may remove the ripple from the animating ripple
+ // array, so cache the reference before nulling it out.
+ final Ripple ripple = ripples[i];
ripples[i] = null;
+ ripple.cancel();
}
mAnimatingRipplesCount = 0;
@@ -470,16 +468,23 @@
@Override
public void draw(Canvas canvas) {
- final Rect bounds = isProjected() ? getDirtyBounds() : getBounds();
+ final boolean isProjected = isProjected();
+ final boolean hasMask = mMask != null;
+ final boolean drawNonMaskContent = mLayerState.mNum > (hasMask ? 1 : 0);
+ final boolean drawMask = hasMask && mMask.getOpacity() != PixelFormat.OPAQUE;
+ final Rect bounds = isProjected ? getDirtyBounds() : getBounds();
- // Draw the content into a layer first.
- final int contentLayer = drawContentLayer(canvas, bounds, SRC_OVER);
+ // If we have content, draw it into a layer first.
+ final int contentLayer = drawNonMaskContent ?
+ drawContentLayer(canvas, bounds, SRC_OVER) : -1;
- // Next, draw the ripples into a layer.
- final int rippleLayer = drawRippleLayer(canvas, bounds, mState.mTintXfermode);
+ // Next, try to draw the ripples (into a layer if necessary). If we need
+ // to mask against the underlying content, set the xfermode to SRC_ATOP.
+ final PorterDuffXfermode xfermode = (hasMask || !drawNonMaskContent) ? SRC_OVER : SRC_ATOP;
+ final int rippleLayer = drawRippleLayer(canvas, bounds, xfermode);
- // If we have ripples, draw the masking layer.
- if (rippleLayer >= 0) {
+ // If we have ripples and a non-opaque mask, draw the masking layer.
+ if (rippleLayer >= 0 && drawMask) {
drawMaskingLayer(canvas, bounds, DST_IN);
}
@@ -523,17 +528,27 @@
}
private int drawContentLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
+ final ChildDrawable[] array = mLayerState.mChildren;
final int count = mLayerState.mNum;
- if (count == 0 || (mState.mMask != null && count == 1)) {
- return -1;
+
+ // We don't need a layer if we don't expect to draw any ripples, we have
+ // an explicit mask, or if the non-mask content is all opaque.
+ boolean needsLayer = false;
+ if (mAnimatingRipplesCount > 0 && mMask == null) {
+ for (int i = 0; i < count; i++) {
+ if (array[i].mId != R.id.mask
+ && array[i].mDrawable.getOpacity() != PixelFormat.OPAQUE) {
+ needsLayer = true;
+ break;
+ }
+ }
}
final Paint maskingPaint = getMaskingPaint(mode);
- final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
- bounds.right, bounds.bottom, maskingPaint);
+ final int restoreToCount = needsLayer ? canvas.saveLayer(bounds.left, bounds.top,
+ bounds.right, bounds.bottom, maskingPaint) : -1;
// Draw everything except the mask.
- final ChildDrawable[] array = mLayerState.mChildren;
for (int i = 0; i < count; i++) {
if (array[i].mId != R.id.mask) {
array[i].mDrawable.draw(canvas);
@@ -552,20 +567,21 @@
// Separate the ripple color and alpha channel. The alpha will be
// applied when we merge the ripples down to the canvas.
final int rippleARGB;
- if (mState.mTint != null) {
- rippleARGB = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+ if (mState.mColor != null) {
+ rippleARGB = mState.mColor.getColorForState(getState(), Color.TRANSPARENT);
} else {
rippleARGB = Color.TRANSPARENT;
}
- final int rippleAlpha = Color.alpha(rippleARGB);
- final int rippleColor = rippleARGB | (0xFF << 24);
if (mRipplePaint == null) {
mRipplePaint = new Paint();
mRipplePaint.setAntiAlias(true);
}
+
+ final int rippleAlpha = Color.alpha(rippleARGB);
final Paint ripplePaint = mRipplePaint;
- ripplePaint.setColor(rippleColor);
+ ripplePaint.setColor(rippleARGB);
+ ripplePaint.setAlpha(0xFF);
boolean drewRipples = false;
int restoreToCount = -1;
@@ -583,7 +599,6 @@
maskingPaint.setAlpha(rippleAlpha);
restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
bounds.right, bounds.bottom, maskingPaint);
- maskingPaint.setAlpha(255);
restoreTranslate = canvas.save();
// Translate the canvas to the current hotspot bounds.
@@ -608,15 +623,13 @@
}
private int drawMaskingLayer(Canvas canvas, Rect bounds, PorterDuffXfermode mode) {
- final Drawable mask = mState.mMask;
- if (mask == null) {
- return -1;
- }
-
final int restoreToCount = canvas.saveLayer(bounds.left, bounds.top,
bounds.right, bounds.bottom, getMaskingPaint(mode));
- mask.draw(canvas);
+ // Ensure that DST_IN blends using the entire layer.
+ canvas.drawColor(Color.TRANSPARENT);
+
+ mMask.draw(canvas);
return restoreToCount;
}
@@ -626,6 +639,7 @@
mMaskingPaint = new Paint();
}
mMaskingPaint.setXfermode(xfermode);
+ mMaskingPaint.setAlpha(0xFF);
return mMaskingPaint;
}
@@ -659,32 +673,19 @@
static class RippleState extends LayerState {
int[] mTouchThemeAttrs;
- ColorStateList mTint = null;
- PorterDuffXfermode mTintXfermode = SRC_ATOP;
- Drawable mMask;
+ ColorStateList mColor = null;
int mMaxRadius = RADIUS_AUTO;
- boolean mPinned = false;
public RippleState(RippleState orig, RippleDrawable owner, Resources res) {
super(orig, owner, res);
if (orig != null) {
mTouchThemeAttrs = orig.mTouchThemeAttrs;
- mTint = orig.mTint;
- mTintXfermode = orig.mTintXfermode;
+ mColor = orig.mColor;
mMaxRadius = orig.mMaxRadius;
- mPinned = orig.mPinned;
}
}
- public void setTintMode(Mode mode) {
- mTintXfermode = new PorterDuffXfermode(mode);
- }
-
- public PorterDuffXfermode getTintXfermode() {
- return mTintXfermode;
- }
-
@Override
public boolean canApplyTheme() {
return mTouchThemeAttrs != null || super.canApplyTheme();
@@ -757,8 +758,6 @@
}
mState = ns;
- mState.mMask = findDrawableByLayerId(R.id.mask);
-
mLayerState = ns;
if (ns.mNum > 0) {
@@ -768,5 +767,12 @@
if (needsTheme) {
applyTheme(theme);
}
+
+ initializeFromState();
+ }
+
+ private void initializeFromState() {
+ // Initialize from constant state.
+ mMask = findDrawableByLayerId(R.id.mask);
}
}
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 5f9d1cd..06aeb98 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -438,7 +438,7 @@
continue;
}
- if ((drawable = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme)) == null) {
+ if ((drawable = Drawable.createFromXmlInner(r, parser, attrs, theme)) == null) {
Log.w("drawable", "Bad element under <rotate>: "
+ parser .getName());
}
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 4c4d9af..f090c11 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -108,7 +108,7 @@
if (type != XmlPullParser.START_TAG) {
continue;
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
if (dr == null) {
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 99ab4dd..024f77c 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -291,41 +291,10 @@
return mShapeState.mAlpha;
}
- /**
- * Specifies a tint for this drawable.
- * <p>
- * Setting a color filter via {@link #setColorFilter(ColorFilter)} overrides
- * tint.
- *
- * @param tint Color state list to use for tinting this drawable, or null to
- * clear the tint
- */
- public void setTint(ColorStateList tint) {
- if (mShapeState.mTint != tint) {
+ @Override
+ public void setTint(ColorStateList tint, Mode tintMode) {
+ if (mShapeState.mTint != tint || mShapeState.mTintMode != tintMode) {
mShapeState.mTint = tint;
- updateTintFilter();
- invalidateSelf();
- }
- }
-
- /**
- * Returns the tint color for this drawable.
- *
- * @return Color state list to use for tinting this drawable, or null if
- * none set
- */
- public ColorStateList getTint() {
- return mShapeState.mTint;
- }
-
- /**
- * Specifies the blending mode used to apply tint.
- *
- * @param tintMode A Porter-Duff blending mode
- * @hide Pending finalization of supported Modes
- */
- public void setTintMode(Mode tintMode) {
- if (mShapeState.mTintMode != tintMode) {
mShapeState.mTintMode = tintMode;
updateTintFilter();
invalidateSelf();
@@ -350,16 +319,6 @@
}
}
- /**
- * Returns the blending mode used to apply tint.
- *
- * @return The Porter-Duff blending mode used to apply tint.
- * @hide Pending finalization of supported Modes
- */
- public Mode getTintMode() {
- return mShapeState.mTintMode;
- }
-
@Override
public void setColorFilter(ColorFilter cf) {
mShapeState.mPaint.setColorFilter(cf);
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index f22a063..b2fac9b 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -185,7 +185,7 @@
+ ": <item> tag requires a 'drawable' attribute or "
+ "child tag defining a drawable");
}
- dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
}
mStateListState.addStateSet(states, dr);
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index afd529c..c531c22 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -18,6 +18,7 @@
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -26,6 +27,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -39,7 +41,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Stack;
/**
* This lets you create a drawable based on an XML vector graphic It can be
@@ -129,9 +131,11 @@
private static final int LINEJOIN_ROUND = 1;
private static final int LINEJOIN_BEVEL = 2;
+ private static final boolean DBG_VECTOR_DRAWABLE = false;
+
private final VectorDrawableState mVectorState;
- private int mAlpha = 0xFF;
+ private final ArrayMap<String, Object> mVGTargetsMap = new ArrayMap<String, Object>();
public VectorDrawable() {
mVectorState = new VectorDrawableState(null);
@@ -145,6 +149,10 @@
}
}
+ Object getTargetByName(String name) {
+ return mVGTargetsMap.get(name);
+ }
+
@Override
public ConstantState getConstantState() {
return mVectorState;
@@ -160,10 +168,14 @@
}
@Override
+ public int getAlpha() {
+ return mVectorState.mVPathRenderer.getRootAlpha();
+ }
+
+ @Override
public void setAlpha(int alpha) {
- // TODO correct handling of transparent
- if (mAlpha != alpha) {
- mAlpha = alpha;
+ if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) {
+ mVectorState.mVPathRenderer.setRootAlpha(alpha);
invalidateSelf();
}
}
@@ -270,6 +282,13 @@
return null;
}
+ private static int applyAlpha(int color, float alpha) {
+ int alphaBytes = Color.alpha(color);
+ color &= 0x00FFFFFF;
+ color |= ((int) (alphaBytes * alpha)) << 24;
+ return color;
+ }
+
private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
final VPathRenderer pathRenderer = new VPathRenderer();
@@ -279,16 +298,24 @@
boolean noGroupTag = true;
boolean noPathTag = true;
- VGroup currentGroup = new VGroup();
+ // Use a stack to help to build the group tree.
+ // The top of the stack is always the current group.
+ final Stack<VGroup> groupStack = new Stack<VGroup>();
+ groupStack.push(pathRenderer.mRootGroup);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
final String tagName = parser.getName();
+ final VGroup currentGroup = groupStack.peek();
+
if (SHAPE_PATH.equals(tagName)) {
final VPath path = new VPath();
path.inflate(res, attrs, theme);
currentGroup.add(path);
+ if (path.getPathName() != null) {
+ mVGTargetsMap.put(path.getPathName(), path);
+ }
noPathTag = false;
} else if (SHAPE_SIZE.equals(tagName)) {
pathRenderer.parseSize(res, attrs);
@@ -297,18 +324,27 @@
pathRenderer.parseViewport(res, attrs);
noViewportTag = false;
} else if (SHAPE_GROUP.equals(tagName)) {
- currentGroup = new VGroup();
- currentGroup.inflate(res, attrs, theme);
- pathRenderer.mGroupList.add(currentGroup);
+ VGroup newChildGroup = new VGroup();
+ newChildGroup.inflate(res, attrs, theme);
+ currentGroup.mChildGroupList.add(newChildGroup);
+ groupStack.push(newChildGroup);
+ if (newChildGroup.getGroupName() != null) {
+ mVGTargetsMap.put(newChildGroup.getGroupName(), newChildGroup);
+ }
noGroupTag = false;
}
+ } else if (eventType == XmlPullParser.END_TAG) {
+ final String tagName = parser.getName();
+ if (SHAPE_GROUP.equals(tagName)) {
+ groupStack.pop();
+ }
}
-
eventType = parser.next();
}
- if (noGroupTag && !noPathTag) {
- pathRenderer.mGroupList.add(currentGroup);
+ // Print the tree out for debug.
+ if (DBG_VECTOR_DRAWABLE) {
+ printGroupTree(pathRenderer.mRootGroup, 0);
}
if (noSizeTag || noViewportTag || noPathTag) {
@@ -338,6 +374,21 @@
return pathRenderer;
}
+ private void printGroupTree(VGroup currentGroup, int level) {
+ String indent = "";
+ for (int i = 0 ; i < level ; i++) {
+ indent += " ";
+ }
+ // Print the current node
+ Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName()
+ + " rotation is " + currentGroup.mRotate);
+ Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString());
+ // Then print all the children
+ for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) {
+ printGroupTree(currentGroup.mChildGroupList.get(i), level + 1);
+ }
+ }
+
private void setPathRenderer(VPathRenderer pathRenderer) {
mVectorState.mVPathRenderer = pathRenderer;
}
@@ -350,6 +401,7 @@
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
mChangingConfigurations = copy.mChangingConfigurations;
+ // TODO: Make sure the constant state are handled correctly.
mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
mPadding = new Rect(copy.mPadding);
}
@@ -377,28 +429,51 @@
}
private static class VPathRenderer {
+ /* Right now the internal data structure is organized as a tree.
+ * Each node can be a group node, or a path.
+ * A group node can have groups or paths as children, but a path node has
+ * no children.
+ * One example can be:
+ * Root Group
+ * / | \
+ * Group Path Group
+ * / \ |
+ * Path Path Path
+ *
+ */
+ private final VGroup mRootGroup;
+
private final Path mPath = new Path();
private final Path mRenderPath = new Path();
- private final Matrix mMatrix = new Matrix();
+ private static final Matrix IDENTITY_MATRIX = new Matrix();
private Paint mStrokePaint;
private Paint mFillPaint;
private ColorFilter mColorFilter;
private PathMeasure mPathMeasure;
- final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>();
+ private float mBaseWidth = 0;
+ private float mBaseHeight = 0;
+ private float mViewportWidth = 0;
+ private float mViewportHeight = 0;
+ private int mRootAlpha = 0xFF;
- float mBaseWidth = 0;
- float mBaseHeight = 0;
- float mViewportWidth = 0;
- float mViewportHeight = 0;
+ private final Matrix mFinalPathMatrix = new Matrix();
public VPathRenderer() {
+ mRootGroup = new VGroup();
+ }
+
+ public void setRootAlpha(int alpha) {
+ mRootAlpha = alpha;
+ }
+
+ public int getRootAlpha() {
+ return mRootAlpha;
}
public VPathRenderer(VPathRenderer copy) {
- mGroupList.addAll(copy.mGroupList);
-
+ mRootGroup = copy.mRootGroup;
mBaseWidth = copy.mBaseWidth;
mBaseHeight = copy.mBaseHeight;
mViewportWidth = copy.mViewportHeight;
@@ -406,33 +481,59 @@
}
public boolean canApplyTheme() {
- final ArrayList<VGroup> groups = mGroupList;
- for (int i = groups.size() - 1; i >= 0; i--) {
- final ArrayList<VPath> paths = groups.get(i).mVGList;
- for (int j = paths.size() - 1; j >= 0; j--) {
- final VPath path = paths.get(j);
- if (path.canApplyTheme()) {
- return true;
- }
+ // If one of the paths can apply theme, then return true;
+ return recursiveCanApplyTheme(mRootGroup);
+ }
+
+ private boolean recursiveCanApplyTheme(VGroup currentGroup) {
+ // We can do a tree traverse here, if there is one path return true,
+ // then we return true for the whole tree.
+ final ArrayList<VPath> paths = currentGroup.mPathList;
+ for (int j = paths.size() - 1; j >= 0; j--) {
+ final VPath path = paths.get(j);
+ if (path.canApplyTheme()) {
+ return true;
}
}
+ final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList;
+
+ for (int i = 0; i < childGroups.size(); i++) {
+ VGroup childGroup = childGroups.get(i);
+ if (childGroup.canApplyTheme()
+ || recursiveCanApplyTheme(childGroup)) {
+ return true;
+ }
+ }
return false;
}
public void applyTheme(Theme t) {
- final ArrayList<VGroup> groups = mGroupList;
- for (int i = groups.size() - 1; i >= 0; i--) {
- VGroup currentGroup = groups.get(i);
- currentGroup.applyTheme(t);
- final ArrayList<VPath> paths = currentGroup.mVGList;
- for (int j = paths.size() - 1; j >= 0; j--) {
- final VPath path = paths.get(j);
- if (path.canApplyTheme()) {
- path.applyTheme(t);
- }
+ // Apply theme to every path of the tree.
+ recursiveApplyTheme(mRootGroup, t);
+ }
+
+ private void recursiveApplyTheme(VGroup currentGroup, Theme t) {
+ // We can do a tree traverse here, apply theme to all paths which
+ // can apply theme.
+ final ArrayList<VPath> paths = currentGroup.mPathList;
+ for (int j = paths.size() - 1; j >= 0; j--) {
+ final VPath path = paths.get(j);
+ if (path.canApplyTheme()) {
+ path.applyTheme(t);
}
}
+
+ final ArrayList<VGroup> childGroups = currentGroup.mChildGroupList;
+
+ for (int i = 0; i < childGroups.size(); i++) {
+ VGroup childGroup = childGroups.get(i);
+ if (childGroup.canApplyTheme()) {
+ childGroup.applyTheme(t);
+ }
+ recursiveApplyTheme(childGroup, t);
+ }
+
}
public void setColorFilter(ColorFilter colorFilter) {
@@ -448,34 +549,36 @@
}
- public void draw(Canvas canvas, int w, int h) {
- if (mGroupList == null || mGroupList.size() == 0) {
- Log.e(LOGTAG,"There is no group to draw");
- return;
- }
+ private void drawGroupTree(VGroup currentGroup, Matrix currentMatrix,
+ float currentAlpha, Canvas canvas, int w, int h) {
+ // Calculate current group's matrix by preConcat the parent's and
+ // and the current one on the top of the stack.
+ // Basically the Mfinal = Mviewport * M0 * M1 * M2;
+ // Mi the local matrix at level i of the group tree.
+ currentGroup.mStackedMatrix.set(currentMatrix);
- for (int i = 0; i < mGroupList.size(); i++) {
- VGroup currentGroup = mGroupList.get(i);
- if (currentGroup != null) {
- drawPath(currentGroup, canvas, w, h);
- }
+ currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix);
+
+ float stackedAlpha = currentAlpha * currentGroup.mGroupAlpha;
+ drawPath(currentGroup, stackedAlpha, canvas, w, h);
+ // Draw the group tree in post order.
+ for (int i = 0 ; i < currentGroup.mChildGroupList.size(); i++) {
+ drawGroupTree(currentGroup.mChildGroupList.get(i),
+ currentGroup.mStackedMatrix, stackedAlpha, canvas, w, h);
}
}
- private void drawPath(VGroup vGroup, Canvas canvas, int w, int h) {
+ public void draw(Canvas canvas, int w, int h) {
+ // Travese the tree in pre-order to draw.
+ drawGroupTree(mRootGroup, IDENTITY_MATRIX, ((float) mRootAlpha) / 0xFF, canvas, w, h);
+ }
+
+ private void drawPath(VGroup vGroup, float stackedAlpha, Canvas canvas, int w, int h) {
final float scale = Math.min(h / mViewportHeight, w / mViewportWidth);
- mMatrix.reset();
-
- // The order we apply is the same as the
- // RenderNode.cpp::applyViewPropertyTransforms().
- mMatrix.postTranslate(-vGroup.mPivotX, -vGroup.mPivotY);
- mMatrix.postScale(vGroup.mScaleX, vGroup.mScaleY);
- mMatrix.postRotate(vGroup.mRotate, 0, 0);
- mMatrix.postTranslate(vGroup.mTranslateX + vGroup.mPivotX, vGroup.mTranslateY + vGroup.mPivotY);
-
- mMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f);
- mMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
+ mFinalPathMatrix.set(vGroup.mStackedMatrix);
+ mFinalPathMatrix.postScale(scale, scale, mViewportWidth / 2f, mViewportHeight / 2f);
+ mFinalPathMatrix.postTranslate(w / 2f - mViewportWidth / 2f, h / 2f - mViewportHeight / 2f);
ArrayList<VPath> paths = vGroup.getPaths();
for (int i = 0; i < paths.size(); i++) {
@@ -507,45 +610,45 @@
mRenderPath.reset();
- mRenderPath.addPath(path, mMatrix);
+ mRenderPath.addPath(path, mFinalPathMatrix);
if (vPath.mClip) {
canvas.clipPath(mRenderPath, Region.Op.REPLACE);
- }
-
- if (vPath.mFillColor != 0) {
- if (mFillPaint == null) {
- mFillPaint = new Paint();
- mFillPaint.setColorFilter(mColorFilter);
- mFillPaint.setStyle(Paint.Style.FILL);
- mFillPaint.setAntiAlias(true);
+ } else {
+ if (vPath.mFillColor != 0) {
+ if (mFillPaint == null) {
+ mFillPaint = new Paint();
+ mFillPaint.setColorFilter(mColorFilter);
+ mFillPaint.setStyle(Paint.Style.FILL);
+ mFillPaint.setAntiAlias(true);
+ }
+ mFillPaint.setColor(applyAlpha(vPath.mFillColor, stackedAlpha));
+ canvas.drawPath(mRenderPath, mFillPaint);
}
- mFillPaint.setColor(vPath.mFillColor);
- canvas.drawPath(mRenderPath, mFillPaint);
- }
+ if (vPath.mStrokeColor != 0) {
+ if (mStrokePaint == null) {
+ mStrokePaint = new Paint();
+ mStrokePaint.setColorFilter(mColorFilter);
+ mStrokePaint.setStyle(Paint.Style.STROKE);
+ mStrokePaint.setAntiAlias(true);
+ }
- if (vPath.mStrokeColor != 0) {
- if (mStrokePaint == null) {
- mStrokePaint = new Paint();
- mStrokePaint.setColorFilter(mColorFilter);
- mStrokePaint.setStyle(Paint.Style.STROKE);
- mStrokePaint.setAntiAlias(true);
+ final Paint strokePaint = mStrokePaint;
+ if (vPath.mStrokeLineJoin != null) {
+ strokePaint.setStrokeJoin(vPath.mStrokeLineJoin);
+ }
+
+ if (vPath.mStrokeLineCap != null) {
+ strokePaint.setStrokeCap(vPath.mStrokeLineCap);
+ }
+
+ strokePaint.setStrokeMiter(vPath.mStrokeMiterlimit * scale);
+
+ strokePaint.setColor(applyAlpha(vPath.mStrokeColor, stackedAlpha));
+ strokePaint.setStrokeWidth(vPath.mStrokeWidth * scale);
+ canvas.drawPath(mRenderPath, strokePaint);
}
-
- final Paint strokePaint = mStrokePaint;
- if (vPath.mStrokeLineJoin != null) {
- strokePaint.setStrokeJoin(vPath.mStrokeLineJoin);
- }
-
- if (vPath.mStrokeLineCap != null) {
- strokePaint.setStrokeCap(vPath.mStrokeLineCap);
- }
-
- strokePaint.setStrokeMiter(vPath.mStrokeMiterlimit * scale);
- strokePaint.setColor(vPath.mStrokeColor);
- strokePaint.setStrokeWidth(vPath.mStrokeWidth * scale);
- canvas.drawPath(mRenderPath, strokePaint);
}
}
}
@@ -586,9 +689,9 @@
}
- private static class VGroup {
- private final HashMap<String, VPath> mVGPathMap = new HashMap<String, VPath>();
- private final ArrayList<VPath> mVGList = new ArrayList<VPath>();
+ static class VGroup {
+ private final ArrayList<VPath> mPathList = new ArrayList<VPath>();
+ private final ArrayList<VGroup> mChildGroupList = new ArrayList<VGroup>();
private float mRotate = 0;
private float mPivotX = 0;
@@ -597,15 +700,122 @@
private float mScaleY = 1;
private float mTranslateX = 0;
private float mTranslateY = 0;
+ private float mGroupAlpha = 1;
+
+ // mLocalMatrix is parsed from the XML.
+ private final Matrix mLocalMatrix = new Matrix();
+ // mStackedMatrix is only used when drawing, it combines all the
+ // parents' local matrices with the current one.
+ private final Matrix mStackedMatrix = new Matrix();
private int[] mThemeAttrs;
+ private String mGroupName = null;
+
+ /* Getter and Setter */
+ public float getRotation() {
+ return mRotate;
+ }
+
+ public void setRotation(float rotation) {
+ if (rotation != mRotate) {
+ mRotate = rotation;
+ updateLocalMatrix();
+ }
+ }
+
+ public float getPivotX() {
+ return mPivotX;
+ }
+
+ public void setPivotX(float pivotX) {
+ if (pivotX != mPivotX) {
+ mPivotX = pivotX;
+ updateLocalMatrix();
+ }
+ }
+
+ public float getPivotY() {
+ return mPivotY;
+ }
+
+ public void setPivotY(float pivotY) {
+ if (pivotY != mPivotY) {
+ mPivotY = pivotY;
+ updateLocalMatrix();
+ }
+ }
+
+ public float getScaleX() {
+ return mScaleX;
+ }
+
+ public void setScaleX(float scaleX) {
+ if (scaleX != mScaleX) {
+ mScaleX = scaleX;
+ updateLocalMatrix();
+ }
+ }
+
+ public float getScaleY() {
+ return mScaleY;
+ }
+
+ public void setScaleY(float scaleY) {
+ if (scaleY != mScaleY) {
+ mScaleY = scaleY;
+ updateLocalMatrix();
+ }
+ }
+
+ public float getTranslateX() {
+ return mTranslateX;
+ }
+
+ public void setTranslateX(float translateX) {
+ if (translateX != mTranslateX) {
+ mTranslateX = translateX;
+ updateLocalMatrix();
+ }
+ }
+
+ public float getTranslateY() {
+ return mTranslateY;
+ }
+
+ public void setTranslateY(float translateY) {
+ if (translateY != mTranslateY) {
+ mTranslateY = translateY;
+ updateLocalMatrix();
+ }
+ }
+
+ public float getAlpha() {
+ return mGroupAlpha;
+ }
+
+ public void setAlpha(float groupAlpha) {
+ if (groupAlpha != mGroupAlpha) {
+ mGroupAlpha = groupAlpha;
+ }
+ }
+
+ public String getGroupName() {
+ return mGroupName;
+ }
+
+ public Matrix getLocalMatrix() {
+ return mLocalMatrix;
+ }
+
public void add(VPath path) {
- String id = path.getID();
- mVGPathMap.put(id, path);
- mVGList.add(path);
+ mPathList.add(path);
}
+ public boolean canApplyTheme() {
+ return mThemeAttrs != null;
+ }
+
public void applyTheme(Theme t) {
if (mThemeAttrs == null) {
return;
@@ -621,6 +831,11 @@
mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY);
mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX);
mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
+ mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha);
+ updateLocalMatrix();
+ if (a.hasValue(R.styleable.VectorDrawableGroup_name)) {
+ mGroupName = a.getString(R.styleable.VectorDrawableGroup_name);
+ }
a.recycle();
}
@@ -660,32 +875,47 @@
mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY);
}
+ if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_name] == 0) {
+ mGroupName = a.getString(R.styleable.VectorDrawableGroup_name);
+ }
+
+ if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawableGroup_alpha] == 0) {
+ mGroupAlpha = a.getFloat(R.styleable.VectorDrawableGroup_alpha, mGroupAlpha);
+ }
+
+ updateLocalMatrix();
a.recycle();
}
+ private void updateLocalMatrix() {
+ // The order we apply is the same as the
+ // RenderNode.cpp::applyViewPropertyTransforms().
+ mLocalMatrix.reset();
+ mLocalMatrix.postTranslate(-mPivotX, -mPivotY);
+ mLocalMatrix.postScale(mScaleX, mScaleY);
+ mLocalMatrix.postRotate(mRotate, 0, 0);
+ mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY);
+ }
+
/**
* Must return in order of adding
* @return ordered list of paths
*/
public ArrayList<VPath> getPaths() {
- return mVGList;
+ return mPathList;
}
}
- private static class VPath {
- private static final int MAX_STATES = 10;
-
+ static class VPath {
private int[] mThemeAttrs;
int mStrokeColor = 0;
float mStrokeWidth = 0;
float mStrokeOpacity = Float.NaN;
-
- int mFillColor = 0;
+ int mFillColor = Color.BLACK;
int mFillRule;
float mFillOpacity = Float.NaN;
-
float mTrimPathStart = 0;
float mTrimPathEnd = 1;
float mTrimPathOffset = 0;
@@ -696,7 +926,7 @@
float mStrokeMiterlimit = 4;
private VNode[] mNode = null;
- private String mId;
+ private String mPathName;
public VPath() {
// Empty constructor.
@@ -709,8 +939,8 @@
}
}
- public String getID() {
- return mId;
+ public String getPathName() {
+ return mPathName;
}
private Paint.Cap getStrokeLineCap(int id, Paint.Cap defValue) {
@@ -739,6 +969,71 @@
}
}
+ /* Setters and Getters */
+ int getStroke() {
+ return mStrokeColor;
+ }
+
+ void setStroke(int strokeColor) {
+ mStrokeColor = strokeColor;
+ }
+
+ float getStrokeWidth() {
+ return mStrokeWidth;
+ }
+
+ void setStrokeWidth(float strokeWidth) {
+ mStrokeWidth = strokeWidth;
+ }
+
+ float getStrokeOpacity() {
+ return mStrokeOpacity;
+ }
+
+ void setStrokeOpacity(float strokeOpacity) {
+ mStrokeOpacity = strokeOpacity;
+ }
+
+ int getFill() {
+ return mFillColor;
+ }
+
+ void setFill(int fillColor) {
+ mFillColor = fillColor;
+ }
+
+ float getFillOpacity() {
+ return mFillOpacity;
+ }
+
+ void setFillOpacity(float fillOpacity) {
+ mFillOpacity = fillOpacity;
+ }
+
+ float getTrimPathStart() {
+ return mTrimPathStart;
+ }
+
+ void setTrimPathStart(float trimPathStart) {
+ mTrimPathStart = trimPathStart;
+ }
+
+ float getTrimPathEnd() {
+ return mTrimPathEnd;
+ }
+
+ void setTrimPathEnd(float trimPathEnd) {
+ mTrimPathEnd = trimPathEnd;
+ }
+
+ float getTrimPathOffset() {
+ return mTrimPathOffset;
+ }
+
+ void setTrimPathOffset(float trimPathOffset) {
+ mTrimPathOffset = trimPathOffset;
+ }
+
public void inflate(Resources r, AttributeSet attrs, Theme theme) {
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawablePath);
final int[] themeAttrs = a.extractThemeAttrs();
@@ -751,7 +1046,7 @@
}
if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_name] == 0) {
- mId = a.getString(R.styleable.VectorDrawablePath_name);
+ mPathName = a.getString(R.styleable.VectorDrawablePath_name);
}
if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pathData] == 0) {
@@ -834,7 +1129,7 @@
mClip = a.getBoolean(R.styleable.VectorDrawablePath_clipToPath, mClip);
if (a.hasValue(R.styleable.VectorDrawablePath_name)) {
- mId = a.getString(R.styleable.VectorDrawablePath_name);
+ mPathName = a.getString(R.styleable.VectorDrawablePath_name);
}
if (a.hasValue(R.styleable.VectorDrawablePath_pathData)) {
@@ -867,13 +1162,11 @@
private void updateColorAlphas() {
if (!Float.isNaN(mFillOpacity)) {
- mFillColor &= 0x00FFFFFF;
- mFillColor |= ((int) (0xFF * mFillOpacity)) << 24;
+ mFillColor = applyAlpha(mFillColor, mFillOpacity);
}
if (!Float.isNaN(mStrokeOpacity)) {
- mStrokeColor &= 0x00FFFFFF;
- mStrokeColor |= ((int) (0xFF * mStrokeOpacity)) << 24;
+ mStrokeColor = applyAlpha(mStrokeColor, mStrokeOpacity);
}
}
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index 21d6caa..4a823cc 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -35,7 +35,7 @@
/**
* This provides the required parameters needed for initializing the
* {@code KeyPairGenerator} that works with
- * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore
+ * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore
* facility</a>. The Android KeyStore facility is accessed through a
* {@link java.security.KeyPairGenerator} API using the {@code AndroidKeyStore}
* provider. The {@code context} passed in may be used to pop up some UI to ask
@@ -306,7 +306,7 @@
* Builder class for {@link KeyPairGeneratorSpec} objects.
* <p>
* This will build a parameter spec for use with the <a href="{@docRoot}
- * guide/topics/security/keystore.html">Android KeyStore facility</a>.
+ * training/articles/keystore.html">Android KeyStore facility</a>.
* <p>
* The required fields must be filled in with the builder.
* <p>
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index b71efc4..2eeb6ad 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -24,7 +24,7 @@
/**
* This provides the optional parameters that can be specified for
* {@code KeyStore} entries that work with
- * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore
+ * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore
* facility</a>. The Android KeyStore facility is accessed through a
* {@link java.security.KeyStore} API using the {@code AndroidKeyStore}
* provider. The {@code context} passed in may be used to pop up some UI to ask
@@ -67,7 +67,7 @@
* Builder class for {@link KeyStoreParameter} objects.
* <p>
* This will build protection parameters for use with the
- * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore
+ * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore
* facility</a>.
* <p>
* This can be used to require that KeyStore entries be stored encrypted.
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 7d4da01..a4b78a6 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3221,7 +3221,7 @@
header->header = (const ResTable_header*) resHeader;
mHeaders.add(header);
- return NO_ERROR;
+ return (mError=NO_ERROR);
}
status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index c1af5f5..937b7c6 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -44,18 +44,18 @@
* @param shadowVertexBuffer Return an floating point array of (x, y, a)
* triangle strips mode.
*/
-VertexBufferMode AmbientShadow::createAmbientShadow(bool isCasterOpaque,
+void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
const int rays = SHADOW_RAY_COUNT;
- VertexBufferMode mode = kVertexBufferMode_OnePolyRingShadow;
+ VertexBuffer::Mode mode = VertexBuffer::kOnePolyRingShadow;
// Validate the inputs.
if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
|| geomFactor <= 0) {
#if DEBUG_SHADOW
ALOGW("Invalid input for createAmbientShadow(), early return!");
#endif
- return mode; // vertex buffer is empty, so any mode doesn't matter.
+ return;
}
Vector<Vector2> dir; // TODO: use C++11 unique_ptr
@@ -127,7 +127,7 @@
// If caster isn't opaque, we need to to fill the umbra by storing the umbra's
// centroid in the innermost ring of vertices.
if (!isCasterOpaque) {
- mode = kVertexBufferMode_TwoPolyRingShadow;
+ mode = VertexBuffer::kTwoPolyRingShadow;
float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
AlphaVertex centroidXYA;
AlphaVertex::set(¢roidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
@@ -135,6 +135,7 @@
shadowVertices[2 * rays + rayIndex] = centroidXYA;
}
}
+ shadowVertexBuffer.setMode(mode);
#if DEBUG_SHADOW
for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) {
@@ -142,7 +143,6 @@
shadowVertices[i].y, shadowVertices[i].alpha);
}
#endif
- return mode;
}
/**
diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h
index 451bfbe..68df246 100644
--- a/libs/hwui/AmbientShadow.h
+++ b/libs/hwui/AmbientShadow.h
@@ -35,7 +35,7 @@
*/
class AmbientShadow {
public:
- static VertexBufferMode createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
+ static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& centroid3d, float heightFactor,
float geomFactor, VertexBuffer& shadowVertexBuffer);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index e5c8898..02e85fe 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -14,6 +14,7 @@
AmbientShadow.cpp \
Animator.cpp \
AssetAtlas.cpp \
+ DamageAccumulator.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
@@ -51,6 +52,7 @@
SpotShadow.cpp \
StatefulBaseRenderer.cpp \
Stencil.cpp \
+ TessellationCache.cpp \
Texture.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index a0c7c55..203cdff 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -117,7 +117,7 @@
virtual void setValue(RenderNode* target, float value);
private:
- typedef void (RenderProperties::*SetFloatProperty)(float value);
+ typedef bool (RenderProperties::*SetFloatProperty)(float value);
typedef float (RenderProperties::*GetFloatProperty)() const;
struct PropertyAccessors;
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 77ef637..6fd9999 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -273,6 +273,8 @@
gradientCache.getSize(), gradientCache.getMaxSize());
log.appendFormat(" PathCache %8d / %8d\n",
pathCache.getSize(), pathCache.getMaxSize());
+ log.appendFormat(" TessellationCache %8d / %8d\n",
+ tessellationCache.getSize(), tessellationCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
log.appendFormat(" PatchCache %8d / %8d\n",
@@ -295,6 +297,7 @@
total += renderBufferCache.getSize();
total += gradientCache.getSize();
total += pathCache.getSize();
+ total += tessellationCache.getSize();
total += dropShadowCache.getSize();
total += patchCache.getSize();
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
@@ -358,6 +361,7 @@
fontRenderer->flush();
textureCache.flush();
pathCache.clear();
+ tessellationCache.clear();
// fall through
case kFlushMode_Layers:
layerCache.clear();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 5367663..b4b5927 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -43,6 +43,7 @@
#include "PatchCache.h"
#include "ProgramCache.h"
#include "PathCache.h"
+#include "TessellationCache.h"
#include "TextDropShadowCache.h"
#include "FboCache.h"
#include "ResourceCache.h"
@@ -326,6 +327,7 @@
ProgramCache programCache;
PathCache pathCache;
PatchCache patchCache;
+ TessellationCache tessellationCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
ResourceCache resourceCache;
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
new file mode 100644
index 0000000..1cb87f2
--- /dev/null
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -0,0 +1,214 @@
+/*
+ * 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 "DamageAccumulator"
+
+#include "DamageAccumulator.h"
+
+#include <cutils/log.h>
+
+#include "RenderNode.h"
+#include "utils/MathUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+NullDamageAccumulator NullDamageAccumulator::sInstance;
+
+NullDamageAccumulator* NullDamageAccumulator::instance() {
+ return &sInstance;
+}
+
+enum TransformType {
+ TransformRenderNode,
+ TransformMatrix4,
+ TransformNone,
+};
+
+struct DirtyStack {
+ TransformType type;
+ union {
+ const RenderNode* renderNode;
+ const Matrix4* matrix4;
+ };
+ // When this frame is pop'd, this rect is mapped through the above transform
+ // and applied to the previous (aka parent) frame
+ SkRect pendingDirty;
+ DirtyStack* prev;
+ DirtyStack* next;
+};
+
+DamageAccumulator::DamageAccumulator() {
+ mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ memset(mHead, 0, sizeof(DirtyStack));
+ // Create a root that we will not pop off
+ mHead->prev = mHead;
+}
+
+void DamageAccumulator::pushCommon() {
+ if (!mHead->next) {
+ DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ nextFrame->next = 0;
+ nextFrame->prev = mHead;
+ mHead->next = nextFrame;
+ }
+ mHead = mHead->next;
+ mHead->pendingDirty.setEmpty();
+}
+
+void DamageAccumulator::pushTransform(const RenderNode* transform) {
+ pushCommon();
+ mHead->type = TransformRenderNode;
+ mHead->renderNode = transform;
+}
+
+void DamageAccumulator::pushTransform(const Matrix4* transform) {
+ pushCommon();
+ mHead->type = TransformMatrix4;
+ mHead->matrix4 = transform;
+}
+
+void DamageAccumulator::pushNullTransform() {
+ pushCommon();
+ mHead->type = TransformNone;
+}
+
+void DamageAccumulator::popTransform() {
+ LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
+ DirtyStack* dirtyFrame = mHead;
+ mHead = mHead->prev;
+ switch (dirtyFrame->type) {
+ case TransformRenderNode:
+ applyRenderNodeTransform(dirtyFrame);
+ break;
+ case TransformMatrix4:
+ applyMatrix4Transform(dirtyFrame);
+ break;
+ case TransformNone:
+ mHead->pendingDirty.join(dirtyFrame->pendingDirty);
+ break;
+ }
+}
+
+static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
+ if (in.isEmpty()) return;
+ Rect temp(in);
+ matrix->mapRect(temp);
+ out->join(RECT_ARGS(temp));
+}
+
+void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
+ mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
+}
+
+static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
+ if (in.isEmpty()) return;
+ const SkMatrix* transform = props.getTransformMatrix();
+ SkRect temp(in);
+ if (transform && !transform->isIdentity()) {
+ transform->mapRect(&temp);
+ }
+ temp.offset(props.getLeft(), props.getTop());
+ out->join(temp);
+}
+
+static DirtyStack* findParentRenderNode(DirtyStack* frame) {
+ while (frame->prev != frame) {
+ frame = frame->prev;
+ if (frame->type == TransformRenderNode) {
+ return frame;
+ }
+ }
+ return NULL;
+}
+
+static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
+ if (frame) {
+ while (frame->prev != frame) {
+ frame = frame->prev;
+ if (frame->type == TransformRenderNode
+ && frame->renderNode->hasProjectionReceiver()) {
+ return frame;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
+ SkRect* rect = &frame->pendingDirty;
+ while (frame != end) {
+ if (frame->type == TransformRenderNode) {
+ mapRect(frame->renderNode->properties(), *rect, rect);
+ } else {
+ mapRect(frame->matrix4, *rect, rect);
+ }
+ frame = frame->prev;
+ }
+}
+
+void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
+ if (frame->pendingDirty.isEmpty()) {
+ return;
+ }
+
+ const RenderProperties& props = frame->renderNode->properties();
+
+ // Perform clipping
+ if (props.getClipToBounds() && !frame->pendingDirty.isEmpty()) {
+ if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
+ frame->pendingDirty.setEmpty();
+ }
+ }
+
+ // apply all transforms
+ mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
+
+ // project backwards if necessary
+ if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
+ // First, find our parent RenderNode:
+ DirtyStack* parentNode = findParentRenderNode(frame);
+ // Find our parent's projection receiver, which is what we project onto
+ DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
+ if (projectionReceiver) {
+ applyTransforms(frame, projectionReceiver);
+ projectionReceiver->pendingDirty.join(frame->pendingDirty);
+ } else {
+ ALOGW("Failed to find projection receiver? Dropping on the floor...");
+ }
+
+ frame->pendingDirty.setEmpty();
+ }
+}
+
+void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
+ mHead->pendingDirty.join(left, top, right, bottom);
+}
+
+void DamageAccumulator::peekAtDirty(SkRect* dest) {
+ *dest = mHead->pendingDirty;
+}
+
+void DamageAccumulator::finish(SkRect* totalDirty) {
+ LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
+ // Root node never has a transform, so this is the fully mapped dirty rect
+ *totalDirty = mHead->pendingDirty;
+ totalDirty->roundOut();
+ mHead->pendingDirty.setEmpty();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
new file mode 100644
index 0000000..fc9b41b
--- /dev/null
+++ b/libs/hwui/DamageAccumulator.h
@@ -0,0 +1,103 @@
+/*
+ * 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 DAMAGEACCUMULATOR_H
+#define DAMAGEACCUMULATOR_H
+
+#include <cutils/compiler.h>
+#include <utils/LinearAllocator.h>
+
+#include <SkMatrix.h>
+#include <SkRect.h>
+
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+struct DirtyStack;
+class RenderNode;
+class Matrix4;
+
+class IDamageAccumulator {
+public:
+ virtual void pushTransform(const RenderNode* transform) = 0;
+ virtual void pushTransform(const Matrix4* transform) = 0;
+ virtual void pushNullTransform() = 0;
+ virtual void popTransform() = 0;
+ virtual void dirty(float left, float top, float right, float bottom) = 0;
+ virtual void peekAtDirty(SkRect* dest) = 0;
+protected:
+ virtual ~IDamageAccumulator() {}
+};
+
+class DamageAccumulator : public IDamageAccumulator {
+ PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
+public:
+ DamageAccumulator();
+ // mAllocator will clean everything up for us, no need for a dtor
+
+ // Push a transform node onto the stack. This should be called prior
+ // to any dirty() calls. Subsequent calls to dirty()
+ // will be affected by the transform when popTransform() is called.
+ virtual void pushTransform(const RenderNode* transform);
+ virtual void pushTransform(const Matrix4* transform);
+ // This is used in combination with peekAtDirty to inspect the damage
+ // area of a subtree
+ virtual void pushNullTransform();
+
+ // Pops a transform node from the stack, propagating the dirty rect
+ // up to the parent node. Returns the IDamageTransform that was just applied
+ virtual void popTransform();
+
+ virtual void dirty(float left, float top, float right, float bottom);
+
+ // Returns the current dirty area, *NOT* transformed by pushed transforms
+ virtual void peekAtDirty(SkRect* dest);
+
+ void finish(SkRect* totalDirty);
+
+private:
+ void pushCommon();
+ void applyMatrix4Transform(DirtyStack* frame);
+ void applyRenderNodeTransform(DirtyStack* frame);
+
+ LinearAllocator mAllocator;
+ DirtyStack* mHead;
+};
+
+class NullDamageAccumulator : public IDamageAccumulator {
+ PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator);
+public:
+ virtual void pushTransform(const RenderNode* transform) { }
+ virtual void pushTransform(const Matrix4* transform) { }
+ virtual void pushNullTransform() { }
+ virtual void popTransform() { }
+ virtual void dirty(float left, float top, float right, float bottom) { }
+ virtual void peekAtDirty(SkRect* dest) { dest->setEmpty(); }
+
+ ANDROID_API static NullDamageAccumulator* instance();
+
+private:
+ NullDamageAccumulator() {}
+ ~NullDamageAccumulator() {}
+
+ static NullDamageAccumulator sInstance;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DAMAGEACCUMULATOR_H */
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 97e9bf6..8e99b9a 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -27,8 +27,7 @@
}
DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer)
- : mDisplayList(0)
- , mSurfaceTexture(0)
+ : mSurfaceTexture(0)
, mTransform(0)
, mNeedsGLContextAttach(false)
, mUpdateTexImage(false)
@@ -41,7 +40,6 @@
mColorFilter = SkSafeRef(mLayer->getColorFilter());
mAlpha = mLayer->getAlpha();
mMode = mLayer->getMode();
- mDirtyRect.setEmpty();
if (!mDestroyer) {
mDestroyer = defaultLayerDestroyer;
@@ -60,33 +58,13 @@
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
-void DeferredLayerUpdater::setDisplayList(RenderNode* displayList,
- int left, int top, int right, int bottom) {
- mDisplayList = displayList;
- if (mDirtyRect.isEmpty()) {
- mDirtyRect.set(left, top, right, bottom);
- } else {
- mDirtyRect.unionWith(Rect(left, top, right, bottom));
- }
-}
-
bool DeferredLayerUpdater::apply(TreeInfo& info) {
bool success = true;
// These properties are applied the same to both layer types
mLayer->setColorFilter(mColorFilter);
mLayer->setAlpha(mAlpha, mMode);
- if (mDisplayList.get()) {
- if (mWidth != mLayer->layer.getWidth() || mHeight != mLayer->layer.getHeight()) {
- success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight);
- }
- mLayer->setBlend(mBlend);
- mDisplayList->prepareTree(info);
- mLayer->updateDeferred(mDisplayList.get(),
- mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom);
- mDirtyRect.setEmpty();
- mDisplayList = 0;
- } else if (mSurfaceTexture.get()) {
+ if (mSurfaceTexture.get()) {
if (mNeedsGLContextAttach) {
mNeedsGLContextAttach = false;
mSurfaceTexture->attachToContext(mLayer->getTexture());
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index b7cfe80..5082271 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -74,9 +74,6 @@
mTransform = matrix ? new SkMatrix(*matrix) : 0;
}
- ANDROID_API void setDisplayList(RenderNode* displayList,
- int left, int top, int right, int bottom);
-
ANDROID_API void setPaint(const SkPaint* paint);
ANDROID_API bool apply(TreeInfo& info);
@@ -94,11 +91,6 @@
int mAlpha;
SkXfermode::Mode mMode;
- // Layer type specific properties
- // displayList and surfaceTexture are mutually exclusive, only 1 may be set
- // dirtyRect is only valid if displayList is set
- sp<RenderNode> mDisplayList;
- Rect mDirtyRect;
sp<GLConsumer> mSurfaceTexture;
SkMatrix* mTransform;
bool mNeedsGLContextAttach;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 9212b9de..233f3f0 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1114,6 +1114,15 @@
OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
}
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ DrawStrokableOp::onDefer(renderer, deferInfo, state);
+ if (!mPaint->getPathEffect()) {
+ renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix,
+ mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy, mPaint);
+ }
+ }
+
virtual const char* name() { return "DrawRoundRect"; }
private:
@@ -1533,9 +1542,23 @@
}
}
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ renderer.getCaches().tessellationCache.precacheShadows(&state.mMatrix,
+ renderer.getLocalClipBounds(), isCasterOpaque(), &mOutline,
+ &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius());
+ }
+
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return renderer.drawShadow(mTransformXY, mTransformZ,
- mCasterAlpha, mCasterUnclipped, &mOutline);
+ TessellationCache::vertexBuffer_pair_t buffers;
+ Matrix4 drawTransform;
+ renderer.getMatrix(&drawTransform);
+ renderer.getCaches().tessellationCache.getShadowBuffers(&drawTransform,
+ renderer.getLocalClipBounds(), isCasterOpaque(), &mOutline,
+ &mTransformXY, &mTransformZ, renderer.getLightCenter(), renderer.getLightRadius(),
+ buffers);
+
+ return renderer.drawShadow(mCasterAlpha, buffers.first, buffers.second);
}
virtual void output(int level, uint32_t logFlags) const {
@@ -1545,6 +1568,8 @@
virtual const char* name() { return "DrawShadow"; }
private:
+ bool isCasterOpaque() { return mCasterAlpha >= 1.0f && mCasterUnclipped; }
+
const mat4 mTransformXY;
const mat4 mTransformZ;
const float mCasterAlpha;
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 195b00b..2eaa671 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -153,6 +153,11 @@
// TODO: rename for consistency
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
+protected:
+ // NOTE: must override these to avoid calling into super class, which calls GL. These may be
+ // removed once DisplayListRenderer no longer inherits from OpenGLRenderer
+ virtual void onViewportInitialized() {};
+ virtual void onSnapshotRestored() {};
private:
void insertRestoreToCount();
diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
index 971a66e..2409554 100644
--- a/libs/hwui/DrawProfiler.cpp
+++ b/libs/hwui/DrawProfiler.cpp
@@ -109,7 +109,7 @@
mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
}
-void DrawProfiler::unionDirty(Rect* dirty) {
+void DrawProfiler::unionDirty(SkRect* dirty) {
RETURN_IF_DISABLED();
// Not worth worrying about minimizing the dirty region for debugging, so just
// dirty the entire viewport.
diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h
index c1aa1c6..7c06e5d 100644
--- a/libs/hwui/DrawProfiler.h
+++ b/libs/hwui/DrawProfiler.h
@@ -37,7 +37,7 @@
void markPlaybackEnd();
void finishFrame();
- void unionDirty(Rect* dirty);
+ void unionDirty(SkRect* dirty);
void draw(OpenGLRenderer* canvas);
void dumpData(int fd);
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 2268386..9f2014f 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -417,6 +417,8 @@
}
void Matrix4::mapRect(Rect& r) const {
+ if (isIdentity()) return;
+
if (isSimple()) {
MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index e33a001..1c5c578 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -147,6 +147,7 @@
data[kTranslateX] += x;
data[kTranslateY] += y;
data[kTranslateZ] += z;
+ mType |= kTypeUnknown;
} else {
// Doing a translation will only affect the translate bit of the type
// Save the type
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cd09f86..8f3872a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -287,6 +287,7 @@
// of the current frame
if (getTargetFbo() == 0) {
mCaches.pathCache.trim();
+ mCaches.tessellationCache.trim();
}
if (!suppressErrorChecks()) {
@@ -2390,7 +2391,7 @@
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode,
+status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
const VertexBuffer& vertexBuffer, const SkPaint* paint, bool useOffset) {
// not missing call to quickReject/dirtyLayer, always done at a higher level
if (!vertexBuffer.getVertexCount()) {
@@ -2398,6 +2399,9 @@
return DrawGlInfo::kStatusDone;
}
+ const Rect& bounds = vertexBuffer.getBounds();
+ dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform());
+
int color = paint->getColor();
bool isAA = paint->isAntiAlias();
@@ -2409,7 +2413,7 @@
setupDrawShader(getShader(paint));
setupDrawBlending(paint, isAA);
setupDrawProgram();
- setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0);
+ setupDrawModelView(kModelViewMode_Translate, useOffset, translateX, translateY, 0, 0);
setupDrawColorUniforms(getShader(paint));
setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawShaderUniforms(getShader(paint));
@@ -2429,13 +2433,14 @@
glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
}
- if (mode == kVertexBufferMode_Standard) {
+ const VertexBuffer::Mode mode = vertexBuffer.getMode();
+ if (mode == VertexBuffer::kStandard) {
mCaches.unbindIndicesBuffer();
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
- } else if (mode == kVertexBufferMode_OnePolyRingShadow) {
+ } else if (mode == VertexBuffer::kOnePolyRingShadow) {
mCaches.bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
- } else if (mode == kVertexBufferMode_TwoPolyRingShadow) {
+ } else if (mode == VertexBuffer::kTwoPolyRingShadow) {
mCaches.bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
}
@@ -2460,14 +2465,7 @@
VertexBuffer vertexBuffer;
// TODO: try clipping large paths to viewport
PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
-
- if (hasLayer()) {
- SkRect bounds = path.getBounds();
- PathTessellator::expandBoundsForStroke(bounds, paint);
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
- }
-
- return drawVertexBuffer(kVertexBufferMode_Standard, vertexBuffer, paint);
+ return drawVertexBuffer(vertexBuffer, paint);
}
/**
@@ -2487,18 +2485,15 @@
count &= ~0x3; // round down to nearest four
VertexBuffer buffer;
- SkRect bounds;
- PathTessellator::tessellateLines(points, count, paint, *currentTransform(), bounds, buffer);
+ PathTessellator::tessellateLines(points, count, paint, *currentTransform(), buffer);
+ const Rect& bounds = buffer.getBounds();
- // can't pass paint, since style would be checked for outset. outset done by tessellation.
- if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
return DrawGlInfo::kStatusDone;
}
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
-
bool useOffset = !paint->isAntiAlias();
- return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset);
+ return drawVertexBuffer(buffer, paint, useOffset);
}
status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) {
@@ -2507,18 +2502,15 @@
count &= ~0x1; // round down to nearest two
VertexBuffer buffer;
- SkRect bounds;
- PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), bounds, buffer);
+ PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), buffer);
- // can't pass paint, since style would be checked for outset. outset done by tessellation.
- if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ const Rect& bounds = buffer.getBounds();
+ if (quickRejectSetupScissor(bounds.left, bounds.top, bounds.right, bounds.bottom)) {
return DrawGlInfo::kStatusDone;
}
- dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform());
-
bool useOffset = !paint->isAntiAlias();
- return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset);
+ return drawVertexBuffer(buffer, paint, useOffset);
}
status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -2564,16 +2556,9 @@
return drawShape(left, top, texture, p);
}
- SkPath path;
- SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
- if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
- float outset = p->getStrokeWidth() / 2;
- rect.outset(outset, outset);
- rx += outset;
- ry += outset;
- }
- path.addRoundRect(rect, rx, ry);
- return drawConvexPath(path, p);
+ const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(),
+ right - left, bottom - top, rx, ry, p);
+ return drawVertexBuffer(left, top, *vertexBuffer, p);
}
status_t OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) {
@@ -3192,8 +3177,8 @@
transformXY.mapPoint(point.x, point.y);
}
-status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
- float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter) {
+status_t OpenGLRenderer::drawShadow(float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
// TODO: use quickRejectWithScissor. For now, always force enable scissor.
@@ -3202,77 +3187,14 @@
SkPaint paint;
paint.setAntiAlias(true); // want to use AlphaVertex
- // tessellate caster outline into a 2d polygon
- Vector<Vertex> casterVertices2d;
- const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
- PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
- casterRefinementThresholdSquared, casterVertices2d);
- if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
- ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
- casterVertices2d.size());
- }
-
- if (casterVertices2d.size() == 0) {
- // empty caster polygon computed from path
- return DrawGlInfo::kStatusDone;
- }
-
- // map 2d caster poly into 3d
- const int casterVertexCount = casterVertices2d.size();
- Vector3 casterPolygon[casterVertexCount];
- float minZ = FLT_MAX;
- float maxZ = -FLT_MAX;
- for (int i = 0; i < casterVertexCount; i++) {
- const Vertex& point2d = casterVertices2d[i];
- casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
- mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
- minZ = fmin(minZ, casterPolygon[i].z);
- maxZ = fmax(maxZ, casterPolygon[i].z);
- }
-
- // map the centroid of the caster into 3d
- Vector2 centroid = ShadowTessellator::centroid2d(
- reinterpret_cast<const Vector2*>(casterVertices2d.array()),
- casterVertexCount);
- Vector3 centroid3d(centroid.x, centroid.y, 0);
- mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
-
- // if the caster intersects the z=0 plane, lift it in Z so it doesn't
- if (minZ < SHADOW_MIN_CASTER_Z) {
- float casterLift = SHADOW_MIN_CASTER_Z - minZ;
- for (int i = 0; i < casterVertexCount; i++) {
- casterPolygon[i].z += casterLift;
- }
- centroid3d.z += casterLift;
- }
-
- // Check whether we want to draw the shadow at all by checking the caster's
- // bounds against clip.
- // We only have ortho projection, so we can just ignore the Z in caster for
- // simple rejection calculation.
- Rect localClip = mSnapshot->getLocalClip();
- Rect casterBounds(casterPerimeter->getBounds());
- casterTransformXY.mapRect(casterBounds);
-
- bool isCasterOpaque = (casterAlpha == 1.0f) && casterUnclipped;
- // draw caster's shadows
- if (mCaches.propertyAmbientShadowStrength > 0) {
+ if (ambientShadowVertexBuffer && mCaches.propertyAmbientShadowStrength > 0) {
paint.setARGB(casterAlpha * mCaches.propertyAmbientShadowStrength, 0, 0, 0);
- VertexBuffer ambientShadowVertexBuffer;
- VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow(
- isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
- casterBounds, localClip, maxZ, ambientShadowVertexBuffer);
- drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint);
+ drawVertexBuffer(*ambientShadowVertexBuffer, &paint);
}
- if (mCaches.propertySpotShadowStrength > 0) {
+ if (spotShadowVertexBuffer && mCaches.propertySpotShadowStrength > 0) {
paint.setARGB(casterAlpha * mCaches.propertySpotShadowStrength, 0, 0, 0);
- VertexBuffer spotShadowVertexBuffer;
- VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow(
- isCasterOpaque, casterPolygon, casterVertexCount,
- *currentTransform(), mLightCenter, mLightRadius, casterBounds, localClip,
- spotShadowVertexBuffer);
- drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint);
+ drawVertexBuffer(*spotShadowVertexBuffer, &paint);
}
return DrawGlInfo::kStatusDrew;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0f953a5..346a65c 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -111,12 +111,6 @@
kModelViewMode_TranslateAndScale = 1,
};
-enum VertexBufferMode {
- kVertexBufferMode_Standard = 0,
- kVertexBufferMode_OnePolyRingShadow = 1,
- kVertexBufferMode_TwoPolyRingShadow = 2
-};
-
///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
@@ -213,8 +207,8 @@
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
- status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
- float casterAlpha, bool casterUnclipped, const SkPath* casterPerimeter);
+ status_t drawShadow(float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer);
virtual void resetPaintFilter();
virtual void setupPaintFilter(int clearBits, int setBits);
@@ -348,6 +342,9 @@
}
#endif
+ const Vector3& getLightCenter() const { return mLightCenter; }
+ float getLightRadius() const { return mLightRadius; }
+
protected:
/**
* Perform the setup specific to a frame. This method does not
@@ -661,10 +658,18 @@
* @param paint The paint to render with
* @param useOffset Offset the vertexBuffer (used in drawing non-AA lines)
*/
- status_t drawVertexBuffer(VertexBufferMode mode, const VertexBuffer& vertexBuffer,
+ status_t drawVertexBuffer(float translateX, float translateY, const VertexBuffer& vertexBuffer,
const SkPaint* paint, bool useOffset = false);
/**
+ * Convenience for translating method
+ */
+ status_t drawVertexBuffer(const VertexBuffer& vertexBuffer,
+ const SkPaint* paint, bool useOffset = false) {
+ return drawVertexBuffer(0.0f, 0.0f, vertexBuffer, paint, useOffset);
+ }
+
+ /**
* Renders the convex hull defined by the specified path as a strip of polygons.
*
* @param path The hull of the path to draw
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index d9c06d3..9dd5aa5 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -74,10 +74,6 @@
return JenkinsHashWhiten(hash);
}
-int PathDescription::compare(const PathDescription& rhs) const {
- return memcmp(this, &rhs, sizeof(PathDescription));
-}
-
///////////////////////////////////////////////////////////////////////////////
// Utilities
///////////////////////////////////////////////////////////////////////////////
@@ -163,14 +159,7 @@
} else {
INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
}
- init();
-}
-PathCache::~PathCache() {
- mCache.clear();
-}
-
-void PathCache::init() {
mCache.setOnEntryRemovedListener(this);
GLint maxTextureSize;
@@ -180,6 +169,10 @@
mDebugEnabled = readDebugLevel() & kDebugCaches;
}
+PathCache::~PathCache() {
+ mCache.clear();
+}
+
///////////////////////////////////////////////////////////////////////////////
// Size management
///////////////////////////////////////////////////////////////////////////////
@@ -341,7 +334,7 @@
}
void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
- sp<PathTask> t = static_cast<PathTask* >(task.get());
+ PathTask* t = static_cast<PathTask*>(task.get());
ATRACE_NAME("pathPrecache");
float left, top, offset;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index bcfb367..eee138b 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -26,6 +26,7 @@
#include "Debug.h"
#include "Properties.h"
#include "Texture.h"
+#include "utils/Macros.h"
#include "utils/Pair.h"
class SkBitmap;
@@ -107,6 +108,7 @@
};
struct PathDescription {
+ DESCRIPTION_TYPE(PathDescription);
ShapeType type;
SkPaint::Join join;
SkPaint::Cap cap;
@@ -148,29 +150,6 @@
PathDescription(ShapeType shapeType, const SkPaint* paint);
hash_t hash() const;
-
- int compare(const PathDescription& rhs) const;
-
- bool operator==(const PathDescription& other) const {
- return compare(other) == 0;
- }
-
- bool operator!=(const PathDescription& other) const {
- return compare(other) != 0;
- }
-
- friend inline int strictly_order_type(
- const PathDescription& lhs, const PathDescription& rhs) {
- return lhs.compare(rhs) < 0;
- }
-
- friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) {
- return lhs.compare(rhs);
- }
-
- friend inline hash_t hash_type(const PathDescription& entry) {
- return entry.hash();
- }
};
/**
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 4ef2158..59e15e1 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -57,15 +57,17 @@
#define ROUND_CAP_THRESH 0.25f
#define PI 3.1415926535897932f
-/**
- * Note: this function doesn't account for the AA case with sub-pixel line thickness (not just 0 <
- * width < 1.0, canvas scale factors in as well) so this can't be used for points/lines
- */
-void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint) {
- if (paint->getStyle() != SkPaint::kFill_Style) {
- float outset = paint->getStrokeWidth() * 0.5f;
- if (outset == 0) outset = 0.5f; // account for hairline
- bounds.outset(outset, outset);
+void PathTessellator::extractTessellationScales(const Matrix4& transform,
+ float* scaleX, float* scaleY) {
+ *scaleX = 1.0f;
+ *scaleY = 1.0f;
+ if (CC_UNLIKELY(!transform.isPureTranslate())) {
+ float m00 = transform.data[Matrix4::kScaleX];
+ float m01 = transform.data[Matrix4::kSkewY];
+ float m10 = transform.data[Matrix4::kSkewX];
+ float m11 = transform.data[Matrix4::kScaleY];
+ *scaleX = sqrt(m00 * m00 + m01 * m01);
+ *scaleY = sqrt(m10 * m10 + m11 * m11);
}
}
@@ -94,18 +96,15 @@
halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
// compute inverse scales
if (CC_UNLIKELY(!transform.isPureTranslate())) {
- float m00 = transform.data[Matrix4::kScaleX];
- float m01 = transform.data[Matrix4::kSkewY];
- float m10 = transform.data[Matrix4::kSkewX];
- float m11 = transform.data[Matrix4::kScaleY];
- float scaleX = sqrt(m00 * m00 + m01 * m01);
- float scaleY = sqrt(m10 * m10 + m11 * m11);
+ float scaleX, scaleY;
+ PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
}
if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
2 * halfStrokeWidth < inverseScaleX) {
+ // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline.
maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX;
halfStrokeWidth = 0.0f;
}
@@ -159,10 +158,10 @@
* Outset the bounds of point data (for line endpoints or points) to account for AA stroke
* geometry.
*/
- void expandBoundsForStrokeAA(SkRect& bounds) const {
+ void expandBoundsForStroke(Rect* bounds) const {
float outset = halfStrokeWidth;
if (outset == 0) outset = 0.5f;
- bounds.outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(),
+ bounds->outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(),
outset * inverseScaleY + Vertex::GeometryFudgeFactor());
}
};
@@ -778,21 +777,25 @@
getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer);
}
}
+
+ Rect bounds(path.getBounds());
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
-static void expandRectToCoverVertex(SkRect& rect, float x, float y) {
- rect.fLeft = fminf(rect.fLeft, x);
- rect.fTop = fminf(rect.fTop, y);
- rect.fRight = fmaxf(rect.fRight, x);
- rect.fBottom = fmaxf(rect.fBottom, y);
+static void expandRectToCoverVertex(Rect& rect, float x, float y) {
+ rect.left = fminf(rect.left, x);
+ rect.top = fminf(rect.top, y);
+ rect.right = fmaxf(rect.right, x);
+ rect.bottom = fmaxf(rect.bottom, y);
}
-static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
+static void expandRectToCoverVertex(Rect& rect, const Vertex& vertex) {
expandRectToCoverVertex(rect, vertex.x, vertex.y);
}
template <class TYPE>
static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
- const float* points, int count, SkRect& bounds) {
+ const float* points, int count, Rect& bounds) {
bounds.set(points[0], points[1], points[0], points[1]);
int numPoints = count / 2;
@@ -807,7 +810,7 @@
}
void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint,
- const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+ const mat4& transform, VertexBuffer& vertexBuffer) {
const PaintInfo paintInfo(paint, transform);
// determine point shape
@@ -830,6 +833,7 @@
if (!outlineVertices.size()) return;
+ Rect bounds;
// tessellate, then duplicate outline across points
int numPoints = count / 2;
VertexBuffer tempBuffer;
@@ -843,12 +847,12 @@
}
// expand bounds from vertex coords to pixel data
- paintInfo.expandBoundsForStrokeAA(bounds);
-
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint,
- const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+ const mat4& transform, VertexBuffer& vertexBuffer) {
ATRACE_CALL();
const PaintInfo paintInfo(paint, transform);
@@ -868,6 +872,7 @@
tempVertices.push();
tempVertices.push();
Vertex* tempVerticesData = tempVertices.editArray();
+ Rect bounds;
bounds.set(points[0], points[1], points[0], points[1]);
for (int i = 0; i < count; i += 4) {
Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]);
@@ -892,7 +897,8 @@
}
// expand bounds from vertex coords to pixel data
- paintInfo.expandBoundsForStrokeAA(bounds);
+ paintInfo.expandBoundsForStroke(&bounds);
+ vertexBuffer.setBounds(bounds);
}
///////////////////////////////////////////////////////////////////////////////
@@ -969,11 +975,14 @@
// Bezier approximation
///////////////////////////////////////////////////////////////////////////////
+// Depth at which recursion is aborted
+#define ABORT_DEPTH 20
+
void PathTessellator::recursiveCubicBezierVertices(
float p1x, float p1y, float c1x, float c1y,
float p2x, float p2y, float c2x, float c2y,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
- Vector<Vertex>& outputVertices) {
+ Vector<Vertex>& outputVertices, int depth) {
float dx = p2x - p1x;
float dy = p2y - p1y;
float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -982,7 +991,7 @@
// multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
- if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, p2x, p2y);
} else {
@@ -1006,11 +1015,11 @@
recursiveCubicBezierVertices(
p1x, p1y, p1c1x, p1c1y,
mx, my, p1c1c2x, p1c1c2y,
- sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
recursiveCubicBezierVertices(
mx, my, p2c1c2x, p2c1c2y,
p2x, p2y, p2c2x, p2c2y,
- sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
}
}
@@ -1019,12 +1028,12 @@
float bx, float by,
float cx, float cy,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
- Vector<Vertex>& outputVertices) {
+ Vector<Vertex>& outputVertices, int depth) {
float dx = bx - ax;
float dy = by - ay;
float d = (cx - bx) * dy - (cy - by) * dx;
- if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, bx, by);
} else {
@@ -1038,9 +1047,9 @@
float my = (acy + bcy) * 0.5f;
recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy,
- sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy,
- sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+ sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1);
}
}
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index a215b7a..8ac9a3b 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -29,7 +29,15 @@
class PathTessellator {
public:
- static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint);
+ /**
+ * Populates scaleX and scaleY with the 'tessellation scale' of the transform - the effective X
+ * and Y scales that tessellation will take into account when generating the 1.0 pixel thick
+ * ramp.
+ *
+ * Two instances of the same shape (size, paint, etc.) will only generate the same vertices if
+ * their tessellation scales are equal.
+ */
+ static void extractTessellationScales(const Matrix4& transform, float* scaleX, float* scaleY);
/**
* Populates a VertexBuffer with a tessellated approximation of the input convex path, as a single
@@ -54,11 +62,10 @@
* @param paint The paint the points will be drawn with indicating AA, stroke width & cap
* @param transform The transform the points will be drawn with, used to drive stretch-aware path
* vertex approximation, and correct AA ramp offsetting
- * @param bounds An output rectangle, which returns the total area covered by the output buffer
* @param vertexBuffer The output buffer
*/
static void tessellatePoints(const float* points, int count, const SkPaint* paint,
- const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+ const mat4& transform, VertexBuffer& vertexBuffer);
/**
* Populates a VertexBuffer with a tessellated approximation of lines as a single triangle
@@ -69,11 +76,10 @@
* @param paint The paint the lines will be drawn with indicating AA, stroke width & cap
* @param transform The transform the points will be drawn with, used to drive stretch-aware path
* vertex approximation, and correct AA ramp offsetting
- * @param bounds An output rectangle, which returns the total area covered by the output buffer
* @param vertexBuffer The output buffer
*/
static void tessellateLines(const float* points, int count, const SkPaint* paint,
- const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+ const mat4& transform, VertexBuffer& vertexBuffer);
/**
* Approximates a convex, CW outline into a Vector of 2d vertices.
@@ -99,7 +105,7 @@
float bx, float by,
float cx, float cy,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
- Vector<Vertex> &outputVertices);
+ Vector<Vertex> &outputVertices, int depth = 0);
/*
endpoints p1, p2
@@ -111,7 +117,7 @@
float p2x, float p2y,
float c2x, float c2y,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
- Vector<Vertex> &outputVertices);
+ Vector<Vertex> &outputVertices, int depth = 0);
};
}; // namespace uirenderer
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 12241b8..feaee8e 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -176,6 +176,7 @@
#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
+#define PROPERTY_VERTEX_CACHE_SIZE "ro.hwui.vertex_cache_size"
#define PROPERTY_PATCH_CACHE_SIZE "ro.hwui.patch_cache_size"
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
@@ -222,6 +223,7 @@
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 10.0f
+#define DEFAULT_VERTEX_CACHE_SIZE 1.0f
#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 2ddbbd7..846ebdc 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -186,6 +186,13 @@
bottom += delta;
}
+ void outset(float xdelta, float ydelta) {
+ left -= xdelta;
+ top -= ydelta;
+ right += xdelta;
+ bottom += ydelta;
+ }
+
/**
* Similar to snapToPixelBoundaries, but estimates bounds conservatively to handle GL rounding
* errors.
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index baf372a..378183a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -15,6 +15,7 @@
*/
#define ATRACE_TAG ATRACE_TAG_VIEW
+#define LOG_TAG "RenderNode"
#include "RenderNode.h"
@@ -25,9 +26,12 @@
#include <utils/Trace.h>
+#include "DamageAccumulator.h"
#include "Debug.h"
#include "DisplayListOp.h"
#include "DisplayListLogBuffer.h"
+#include "LayerRenderer.h"
+#include "OpenGLRenderer.h"
#include "utils/MathUtils.h"
namespace android {
@@ -57,12 +61,14 @@
, mNeedsDisplayListDataSync(false)
, mDisplayListData(0)
, mStagingDisplayListData(0)
- , mNeedsAnimatorsSync(false) {
+ , mNeedsAnimatorsSync(false)
+ , mLayer(0) {
}
RenderNode::~RenderNode() {
delete mDisplayListData;
delete mStagingDisplayListData;
+ LayerRenderer::destroyLayerDeferred(mLayer);
}
void RenderNode::setStagingDisplayList(DisplayListData* data) {
@@ -110,14 +116,85 @@
prepareTreeImpl(info);
}
-void RenderNode::prepareTreeImpl(TreeInfo& info) {
- if (info.performStagingPush) {
- pushStagingChanges(info);
+void RenderNode::damageSelf(TreeInfo& info) {
+ if (isRenderable() && properties().getAlpha() > 0) {
+ if (properties().getClipToBounds()) {
+ info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
+ } else {
+ // Hope this is big enough?
+ // TODO: Get this from the display list ops or something
+ info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX);
+ }
}
- if (info.evaluateAnimations) {
+}
+
+void RenderNode::prepareLayer(TreeInfo& info) {
+ LayerType layerType = properties().layerProperties().type();
+ if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) {
+ // We push a null transform here as we don't care what the existing dirty
+ // area is, only what our display list dirty is as well as our children's
+ // dirty area
+ info.damageAccumulator->pushNullTransform();
+ }
+}
+
+void RenderNode::pushLayerUpdate(TreeInfo& info) {
+ LayerType layerType = properties().layerProperties().type();
+ // If we are not a layer OR we cannot be rendered (eg, view was detached)
+ // we need to destroy any Layers we may have had previously
+ if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) {
+ if (layerType == kLayerTypeRenderLayer) {
+ info.damageAccumulator->popTransform();
+ }
+ if (CC_UNLIKELY(mLayer)) {
+ LayerRenderer::destroyLayer(mLayer);
+ mLayer = NULL;
+ }
+ return;
+ }
+
+ if (!mLayer) {
+ mLayer = LayerRenderer::createRenderLayer(getWidth(), getHeight());
+ applyLayerPropertiesToLayer(info);
+ damageSelf(info);
+ } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
+ LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight());
+ damageSelf(info);
+ }
+
+ SkRect dirty;
+ info.damageAccumulator->peekAtDirty(&dirty);
+ info.damageAccumulator->popTransform();
+
+ if (!dirty.isEmpty()) {
+ mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
+ }
+ // This is not inside the above if because we may have called
+ // updateDeferred on a previous prepare pass that didn't have a renderer
+ if (info.renderer && mLayer->deferredUpdateScheduled) {
+ info.renderer->pushLayerUpdate(mLayer);
+ }
+}
+
+void RenderNode::prepareTreeImpl(TreeInfo& info) {
+ info.damageAccumulator->pushTransform(this);
+ if (info.mode == TreeInfo::MODE_FULL) {
+ pushStagingPropertiesChanges(info);
+ evaluateAnimations(info);
+ } else if (info.mode == TreeInfo::MODE_MAYBE_DETACHING) {
+ pushStagingPropertiesChanges(info);
+ } else if (info.mode == TreeInfo::MODE_RT_ONLY) {
evaluateAnimations(info);
}
+
+ prepareLayer(info);
+ if (info.mode == TreeInfo::MODE_FULL) {
+ pushStagingDisplayListChanges(info);
+ }
prepareSubTree(info, mDisplayListData);
+ pushLayerUpdate(info);
+
+ info.damageAccumulator->popTransform();
}
class PushAnimatorsFunctor {
@@ -134,7 +211,7 @@
TreeInfo& mInfo;
};
-void RenderNode::pushStagingChanges(TreeInfo& info) {
+void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
// Push the animators first so that setupStartValueIfNecessary() is called
// before properties() is trampled by stagingProperties(), as they are
// required by some animators.
@@ -149,18 +226,41 @@
}
if (mDirtyPropertyFields) {
mDirtyPropertyFields = 0;
+ damageSelf(info);
+ info.damageAccumulator->popTransform();
mProperties = mStagingProperties;
+ applyLayerPropertiesToLayer(info);
+ // We could try to be clever and only re-damage if the matrix changed.
+ // However, we don't need to worry about that. The cost of over-damaging
+ // here is only going to be a single additional map rect of this node
+ // plus a rect join(). The parent's transform (and up) will only be
+ // performed once.
+ info.damageAccumulator->pushTransform(this);
+ damageSelf(info);
}
+}
+
+void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
+ if (CC_LIKELY(!mLayer)) return;
+
+ const LayerProperties& props = properties().layerProperties();
+ mLayer->setAlpha(props.alpha(), props.xferMode());
+ mLayer->setColorFilter(props.colorFilter());
+ mLayer->setBlend(props.needsBlending());
+}
+
+void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
if (mNeedsDisplayListDataSync) {
mNeedsDisplayListDataSync = false;
// Do a push pass on the old tree to handle freeing DisplayListData
// that are no longer used
- TreeInfo oldTreeInfo;
+ TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING);
+ oldTreeInfo.damageAccumulator = info.damageAccumulator;
prepareSubTree(oldTreeInfo, mDisplayListData);
- // TODO: The damage for the old tree should be accounted for
delete mDisplayListData;
mDisplayListData = mStagingDisplayListData;
mStagingDisplayListData = 0;
+ damageSelf(info);
}
}
@@ -180,12 +280,21 @@
void RenderNode::evaluateAnimations(TreeInfo& info) {
if (!mAnimators.size()) return;
+ // TODO: Can we target this better? For now treat it like any other staging
+ // property push and just damage self before and after animators are run
+
+ damageSelf(info);
+ info.damageAccumulator->popTransform();
+
AnimateFunctor functor(this, info);
std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd;
newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
mAnimators.erase(newEnd, mAnimators.end());
mProperties.updateMatrix();
info.out.hasAnimations |= mAnimators.size();
+
+ info.damageAccumulator->pushTransform(this);
+ damageSelf(info);
}
void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
@@ -201,8 +310,11 @@
info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
}
for (size_t i = 0; i < subtree->children().size(); i++) {
- RenderNode* childNode = subtree->children()[i]->mDisplayList;
+ DrawDisplayListOp* op = subtree->children()[i];
+ RenderNode* childNode = op->mDisplayList;
+ info.damageAccumulator->pushTransform(&op->mTransformFromParent);
childNode->prepareTreeImpl(info);
+ info.damageAccumulator->popTransform();
}
}
}
@@ -234,9 +346,10 @@
renderer.concatMatrix(*properties().getTransformMatrix());
}
}
- bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds();
+ const bool isLayer = properties().layerProperties().type() != kLayerTypeNone;
+ bool clipToBoundsNeeded = isLayer ? false : properties().getClipToBounds();
if (properties().getAlpha() < 1) {
- if (properties().getCaching()) {
+ if (isLayer) {
renderer.setOverrideLayerAlpha(properties().getAlpha());
} else if (!properties().getHasOverlappingRendering()) {
renderer.scaleAlpha(properties().getAlpha());
@@ -651,8 +764,14 @@
*/
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
+ const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
+ // If we are updating the contents of mLayer, we don't want to apply any of
+ // the RenderNode's properties to this issueOperations pass. Those will all
+ // be applied when the layer is drawn, aka when this is true.
+ const bool useViewProperties = (!mLayer || drawLayer);
+
const int level = handler.level();
- if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
+ if (mDisplayListData->isEmpty() || (useViewProperties && properties().getAlpha() <= 0)) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
return;
}
@@ -674,7 +793,9 @@
DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
- setViewProperties<T>(renderer, handler);
+ if (useViewProperties) {
+ setViewProperties<T>(renderer, handler);
+ }
bool quickRejected = properties().getClipToBounds()
&& renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
@@ -683,31 +804,36 @@
renderer.setClippingOutline(alloc, &(mProperties.getOutline()));
}
- Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
- buildZSortedChildList(zTranslatedNodes);
+ if (drawLayer) {
+ handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
+ renderer.getSaveCount() - 1, properties().getClipToBounds());
+ } else {
+ Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+ buildZSortedChildList(zTranslatedNodes);
- // for 3d root, draw children with negative z values
- issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+ // for 3d root, draw children with negative z values
+ issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- const int saveCountOffset = renderer.getSaveCount() - 1;
- const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- DisplayListOp *op = mDisplayListData->displayListOps[i];
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ const int saveCountOffset = renderer.getSaveCount() - 1;
+ const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ DisplayListOp *op = mDisplayListData->displayListOps[i];
-#if DEBUG_DISPLAY_LIST
- op->output(level + 1);
-#endif
- logBuffer.writeCommand(level, op->name());
- handler(op, saveCountOffset, properties().getClipToBounds());
+ #if DEBUG_DISPLAY_LIST
+ op->output(level + 1);
+ #endif
+ logBuffer.writeCommand(level, op->name());
+ handler(op, saveCountOffset, properties().getClipToBounds());
- if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
- issueOperationsOfProjectedChildren(renderer, handler);
+ if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
+ issueOperationsOfProjectedChildren(renderer, handler);
+ }
}
- }
- // for 3d root, draw children with positive z values
- issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+ // for 3d root, draw children with positive z values
+ issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+ }
}
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 1a5377b..b2fe849 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -39,6 +39,7 @@
#include <androidfw/ResourceTypes.h>
+#include "DamageAccumulator.h"
#include "Debug.h"
#include "Matrix.h"
#include "DeferredDisplayList.h"
@@ -125,6 +126,10 @@
return mDisplayListData && mDisplayListData->hasDrawOps;
}
+ bool hasProjectionReceiver() const {
+ return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0;
+ }
+
const char* getName() const {
return mName.string();
}
@@ -148,7 +153,7 @@
mDirtyPropertyFields |= fields;
}
- const RenderProperties& properties() {
+ const RenderProperties& properties() const {
return mProperties;
}
@@ -187,6 +192,9 @@
mNeedsAnimatorsSync = true;
}
+protected:
+ virtual void damageSelf(TreeInfo& info);
+
private:
typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
@@ -246,9 +254,13 @@
};
void prepareTreeImpl(TreeInfo& info);
- void pushStagingChanges(TreeInfo& info);
+ void pushStagingPropertiesChanges(TreeInfo& info);
+ void pushStagingDisplayListChanges(TreeInfo& info);
void evaluateAnimations(TreeInfo& info);
void prepareSubTree(TreeInfo& info, DisplayListData* subtree);
+ void applyLayerPropertiesToLayer(TreeInfo& info);
+ void prepareLayer(TreeInfo& info);
+ void pushLayerUpdate(TreeInfo& info);
String8 mName;
@@ -264,6 +276,10 @@
std::set< sp<BaseRenderNodeAnimator> > mStagingAnimators;
std::vector< sp<BaseRenderNodeAnimator> > mAnimators;
+ // Owned by RT. Lifecycle is managed by prepareTree(), with the exception
+ // being in ~RenderNode() which may happen on any thread.
+ Layer* mLayer;
+
/**
* Draw time state - these properties are only set and used during rendering
*/
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 5f7d4e3..8848b2f 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -21,16 +21,59 @@
#include <utils/Trace.h>
#include <SkCanvas.h>
+#include <SkColorFilter.h>
#include <SkMatrix.h>
#include <SkPath.h>
#include <SkPathOps.h>
#include "Matrix.h"
+#include "OpenGLRenderer.h"
#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
+LayerProperties::LayerProperties()
+ : mType(kLayerTypeNone)
+ , mColorFilter(NULL) {
+ reset();
+}
+
+LayerProperties::~LayerProperties() {
+ setType(kLayerTypeNone);
+}
+
+void LayerProperties::reset() {
+ mOpaque = false;
+ setFromPaint(NULL);
+}
+
+bool LayerProperties::setColorFilter(SkColorFilter* filter) {
+ if (mColorFilter == filter) return false;
+ SkRefCnt_SafeAssign(mColorFilter, filter);
+ return true;
+}
+
+bool LayerProperties::setFromPaint(const SkPaint* paint) {
+ bool changed = false;
+ SkXfermode::Mode mode;
+ int alpha;
+ OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+ changed |= setAlpha(static_cast<uint8_t>(alpha));
+ changed |= setXferMode(mode);
+ changed |= setColorFilter(paint ? paint->getColorFilter() : NULL);
+ return changed;
+}
+
+LayerProperties& LayerProperties::operator=(const LayerProperties& other) {
+ setType(other.type());
+ setOpaque(other.opaque());
+ setAlpha(other.alpha());
+ setXferMode(other.xferMode());
+ setColorFilter(other.colorFilter());
+ return *this;
+}
+
RenderProperties::PrimitiveFields::PrimitiveFields()
: mClipToBounds(true)
, mProjectBackwards(false)
@@ -45,8 +88,7 @@
, mLeft(0), mTop(0), mRight(0), mBottom(0)
, mWidth(0), mHeight(0)
, mPivotExplicitlySet(false)
- , mMatrixOrPivotDirty(false)
- , mCaching(false) {
+ , mMatrixOrPivotDirty(false) {
}
RenderProperties::ComputedFields::ComputedFields()
@@ -73,6 +115,7 @@
setStaticMatrix(other.getStaticMatrix());
setAnimationMatrix(other.getAnimationMatrix());
setCameraDistance(other.getCameraDistance());
+ mLayerProperties = other.layerProperties();
// Force recalculation of the matrix, since other's dirty bit may be clear
mPrimitiveFields.mMatrixOrPivotDirty = true;
@@ -103,9 +146,9 @@
}
}
- bool clipToBoundsNeeded = mPrimitiveFields.mCaching ? false : mPrimitiveFields.mClipToBounds;
+ bool clipToBoundsNeeded = layerProperties().type() != kLayerTypeNone ? false : mPrimitiveFields.mClipToBounds;
if (mPrimitiveFields.mAlpha < 1) {
- if (mPrimitiveFields.mCaching) {
+ if (layerProperties().type() != kLayerTypeNone) {
ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
} else if (!mPrimitiveFields.mHasOverlappingRendering) {
ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index c0e3ce7..8c6cc9e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -32,6 +32,7 @@
#include "Outline.h"
class SkBitmap;
+class SkColorFilter;
class SkPaint;
namespace android {
@@ -39,40 +40,125 @@
class Matrix4;
class RenderNode;
+class RenderProperties;
+
+// The __VA_ARGS__ will be executed if a & b are not equal
+#define RP_SET(a, b, ...) (a != b ? (a = b, ##__VA_ARGS__, true) : false)
+#define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
+
+// Keep in sync with View.java:LAYER_TYPE_*
+enum LayerType {
+ kLayerTypeNone = 0,
+ // Although we cannot build the software layer directly (must be done at
+ // record time), this information is used when applying alpha.
+ kLayerTypeSoftware = 1,
+ kLayerTypeRenderLayer = 2,
+ // TODO: LayerTypeSurfaceTexture? Maybe?
+};
+
+class ANDROID_API LayerProperties {
+public:
+ bool setType(LayerType type) {
+ if (RP_SET(mType, type)) {
+ reset();
+ return true;
+ }
+ return false;
+ }
+
+ LayerType type() const {
+ return mType;
+ }
+
+ bool setOpaque(bool opaque) {
+ return RP_SET(mOpaque, opaque);
+ }
+
+ bool opaque() const {
+ return mOpaque;
+ }
+
+ bool setAlpha(uint8_t alpha) {
+ return RP_SET(mAlpha, alpha);
+ }
+
+ uint8_t alpha() const {
+ return mAlpha;
+ }
+
+ bool setXferMode(SkXfermode::Mode mode) {
+ return RP_SET(mMode, mode);
+ }
+
+ SkXfermode::Mode xferMode() const {
+ return mMode;
+ }
+
+ bool setColorFilter(SkColorFilter* filter);
+
+ SkColorFilter* colorFilter() const {
+ return mColorFilter;
+ }
+
+ // Sets alpha, xfermode, and colorfilter from an SkPaint
+ // paint may be NULL, in which case defaults will be set
+ bool setFromPaint(const SkPaint* paint);
+
+ bool needsBlending() const {
+ return !opaque() || alpha() < 255;
+ }
+
+ LayerProperties& operator=(const LayerProperties& other);
+
+private:
+ LayerProperties();
+ ~LayerProperties();
+ void reset();
+
+ friend class RenderProperties;
+
+ LayerType mType;
+ // Whether or not that Layer's content is opaque, doesn't include alpha
+ bool mOpaque;
+ uint8_t mAlpha;
+ SkXfermode::Mode mMode;
+ SkColorFilter* mColorFilter;
+};
/*
* Data structure that holds the properties for a RenderNode
*/
-class RenderProperties {
+class ANDROID_API RenderProperties {
public:
RenderProperties();
virtual ~RenderProperties();
RenderProperties& operator=(const RenderProperties& other);
- void setClipToBounds(bool clipToBounds) {
- mPrimitiveFields.mClipToBounds = clipToBounds;
+ bool setClipToBounds(bool clipToBounds) {
+ return RP_SET(mPrimitiveFields.mClipToBounds, clipToBounds);
}
- void setProjectBackwards(bool shouldProject) {
- mPrimitiveFields.mProjectBackwards = shouldProject;
+ bool setProjectBackwards(bool shouldProject) {
+ return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
}
- void setProjectionReceiver(bool shouldRecieve) {
- mPrimitiveFields.mProjectionReceiver = shouldRecieve;
+ bool setProjectionReceiver(bool shouldRecieve) {
+ return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve);
}
bool isProjectionReceiver() const {
return mPrimitiveFields.mProjectionReceiver;
}
- void setStaticMatrix(const SkMatrix* matrix) {
+ bool setStaticMatrix(const SkMatrix* matrix) {
delete mStaticMatrix;
if (matrix) {
mStaticMatrix = new SkMatrix(*matrix);
} else {
mStaticMatrix = NULL;
}
+ return true;
}
// Can return NULL
@@ -80,72 +166,61 @@
return mStaticMatrix;
}
- void setAnimationMatrix(const SkMatrix* matrix) {
+ bool setAnimationMatrix(const SkMatrix* matrix) {
delete mAnimationMatrix;
if (matrix) {
mAnimationMatrix = new SkMatrix(*matrix);
} else {
mAnimationMatrix = NULL;
}
+ return true;
}
- void setAlpha(float alpha) {
+ bool setAlpha(float alpha) {
alpha = fminf(1.0f, fmaxf(0.0f, alpha));
- if (alpha != mPrimitiveFields.mAlpha) {
- mPrimitiveFields.mAlpha = alpha;
- }
+ return RP_SET(mPrimitiveFields.mAlpha, alpha);
}
float getAlpha() const {
return mPrimitiveFields.mAlpha;
}
- void setHasOverlappingRendering(bool hasOverlappingRendering) {
- mPrimitiveFields.mHasOverlappingRendering = hasOverlappingRendering;
+ bool setHasOverlappingRendering(bool hasOverlappingRendering) {
+ return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering);
}
bool hasOverlappingRendering() const {
return mPrimitiveFields.mHasOverlappingRendering;
}
- void setElevation(float elevation) {
- if (elevation != mPrimitiveFields.mElevation) {
- mPrimitiveFields.mElevation = elevation;
- // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
- }
+ bool setElevation(float elevation) {
+ return RP_SET(mPrimitiveFields.mElevation, elevation);
+ // Don't dirty matrix/pivot, since they don't respect Z
}
float getElevation() const {
return mPrimitiveFields.mElevation;
}
- void setTranslationX(float translationX) {
- if (translationX != mPrimitiveFields.mTranslationX) {
- mPrimitiveFields.mTranslationX = translationX;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setTranslationX(float translationX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX);
}
float getTranslationX() const {
return mPrimitiveFields.mTranslationX;
}
- void setTranslationY(float translationY) {
- if (translationY != mPrimitiveFields.mTranslationY) {
- mPrimitiveFields.mTranslationY = translationY;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setTranslationY(float translationY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY);
}
float getTranslationY() const {
return mPrimitiveFields.mTranslationY;
}
- void setTranslationZ(float translationZ) {
- if (translationZ != mPrimitiveFields.mTranslationZ) {
- mPrimitiveFields.mTranslationZ = translationZ;
- // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
- }
+ bool setTranslationZ(float translationZ) {
+ return RP_SET(mPrimitiveFields.mTranslationZ, translationZ);
+ // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
}
float getTranslationZ() const {
@@ -153,8 +228,8 @@
}
// Animation helper
- void setX(float value) {
- setTranslationX(value - getLeft());
+ bool setX(float value) {
+ return setTranslationX(value - getLeft());
}
// Animation helper
@@ -163,8 +238,8 @@
}
// Animation helper
- void setY(float value) {
- setTranslationY(value - getTop());
+ bool setY(float value) {
+ return setTranslationY(value - getTop());
}
// Animation helper
@@ -173,87 +248,80 @@
}
// Animation helper
- void setZ(float value) {
- setTranslationZ(value - getElevation());
+ bool setZ(float value) {
+ return setTranslationZ(value - getElevation());
}
float getZ() const {
return getElevation() + getTranslationZ();
}
- void setRotation(float rotation) {
- if (rotation != mPrimitiveFields.mRotation) {
- mPrimitiveFields.mRotation = rotation;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setRotation(float rotation) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation);
}
float getRotation() const {
return mPrimitiveFields.mRotation;
}
- void setRotationX(float rotationX) {
- if (rotationX != mPrimitiveFields.mRotationX) {
- mPrimitiveFields.mRotationX = rotationX;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setRotationX(float rotationX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX);
}
float getRotationX() const {
return mPrimitiveFields.mRotationX;
}
- void setRotationY(float rotationY) {
- if (rotationY != mPrimitiveFields.mRotationY) {
- mPrimitiveFields.mRotationY = rotationY;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setRotationY(float rotationY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY);
}
float getRotationY() const {
return mPrimitiveFields.mRotationY;
}
- void setScaleX(float scaleX) {
- if (scaleX != mPrimitiveFields.mScaleX) {
- mPrimitiveFields.mScaleX = scaleX;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setScaleX(float scaleX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
}
float getScaleX() const {
return mPrimitiveFields.mScaleX;
}
- void setScaleY(float scaleY) {
- if (scaleY != mPrimitiveFields.mScaleY) {
- mPrimitiveFields.mScaleY = scaleY;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setScaleY(float scaleY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
}
float getScaleY() const {
return mPrimitiveFields.mScaleY;
}
- void setPivotX(float pivotX) {
- mPrimitiveFields.mPivotX = pivotX;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- mPrimitiveFields.mPivotExplicitlySet = true;
+ bool setPivotX(float pivotX) {
+ if (RP_SET(mPrimitiveFields.mPivotX, pivotX)
+ || !mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mPrimitiveFields.mPivotExplicitlySet = true;
+ return true;
+ }
+ return false;
}
/* Note that getPivotX and getPivotY are adjusted by updateMatrix(),
- * so the value returned mPrimitiveFields.may be stale if the RenderProperties has been
- * mPrimitiveFields.modified since the last call to updateMatrix()
+ * so the value returned may be stale if the RenderProperties has been
+ * modified since the last call to updateMatrix()
*/
float getPivotX() const {
return mPrimitiveFields.mPivotX;
}
- void setPivotY(float pivotY) {
- mPrimitiveFields.mPivotY = pivotY;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- mPrimitiveFields.mPivotExplicitlySet = true;
+ bool setPivotY(float pivotY) {
+ if (RP_SET(mPrimitiveFields.mPivotY, pivotY)
+ || !mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mPrimitiveFields.mPivotExplicitlySet = true;
+ return true;
+ }
+ return false;
}
float getPivotY() const {
@@ -264,11 +332,13 @@
return mPrimitiveFields.mPivotExplicitlySet;
}
- void setCameraDistance(float distance) {
+ bool setCameraDistance(float distance) {
if (distance != getCameraDistance()) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance);
+ return true;
}
+ return false;
}
float getCameraDistance() const {
@@ -276,75 +346,73 @@
return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ();
}
- void setLeft(int left) {
- if (left != mPrimitiveFields.mLeft) {
- mPrimitiveFields.mLeft = left;
+ bool setLeft(int left) {
+ if (RP_SET(mPrimitiveFields.mLeft, left)) {
mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
float getLeft() const {
return mPrimitiveFields.mLeft;
}
- void setTop(int top) {
- if (top != mPrimitiveFields.mTop) {
- mPrimitiveFields.mTop = top;
+ bool setTop(int top) {
+ if (RP_SET(mPrimitiveFields.mTop, top)) {
mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
float getTop() const {
return mPrimitiveFields.mTop;
}
- void setRight(int right) {
- if (right != mPrimitiveFields.mRight) {
- mPrimitiveFields.mRight = right;
+ bool setRight(int right) {
+ if (RP_SET(mPrimitiveFields.mRight, right)) {
mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
float getRight() const {
return mPrimitiveFields.mRight;
}
- void setBottom(int bottom) {
- if (bottom != mPrimitiveFields.mBottom) {
- mPrimitiveFields.mBottom = bottom;
+ bool setBottom(int bottom) {
+ if (RP_SET(mPrimitiveFields.mBottom, bottom)) {
mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
float getBottom() const {
return mPrimitiveFields.mBottom;
}
- void setLeftTop(int left, int top) {
- if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop) {
- mPrimitiveFields.mLeft = left;
- mPrimitiveFields.mTop = top;
- mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
- mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
- if (!mPrimitiveFields.mPivotExplicitlySet) {
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
- }
+ bool setLeftTop(int left, int top) {
+ bool leftResult = setLeft(left);
+ bool topResult = setTop(top);
+ return leftResult || topResult;
}
- void setLeftTopRightBottom(int left, int top, int right, int bottom) {
+ bool setLeftTopRightBottom(int left, int top, int right, int bottom) {
if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop
|| right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) {
mPrimitiveFields.mLeft = left;
@@ -356,31 +424,27 @@
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
- void offsetLeftRight(float offset) {
+ bool offsetLeftRight(float offset) {
if (offset != 0) {
mPrimitiveFields.mLeft += offset;
mPrimitiveFields.mRight += offset;
- if (!mPrimitiveFields.mPivotExplicitlySet) {
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ return true;
}
+ return false;
}
- void offsetTopBottom(float offset) {
+ bool offsetTopBottom(float offset) {
if (offset != 0) {
mPrimitiveFields.mTop += offset;
mPrimitiveFields.mBottom += offset;
- if (!mPrimitiveFields.mPivotExplicitlySet) {
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ return true;
}
- }
-
- void setCaching(bool caching) {
- mPrimitiveFields.mCaching = caching;
+ return false;
}
int getWidth() const {
@@ -409,10 +473,6 @@
return mComputedFields.mTransformMatrix;
}
- bool getCaching() const {
- return mPrimitiveFields.mCaching;
- }
-
bool getClipToBounds() const {
return mPrimitiveFields.mClipToBounds;
}
@@ -435,7 +495,7 @@
void debugOutputProperties(const int level) const;
- ANDROID_API void updateMatrix();
+ void updateMatrix();
bool hasClippingPath() const {
return mPrimitiveFields.mRevealClip.willClip();
@@ -458,6 +518,14 @@
return mPrimitiveFields.mRevealClip;
}
+ const LayerProperties& layerProperties() const {
+ return mLayerProperties;
+ }
+
+ LayerProperties& mutateLayerProperties() {
+ return mLayerProperties;
+ }
+
private:
// Rendering properties
@@ -480,11 +548,11 @@
int mWidth, mHeight;
bool mPivotExplicitlySet;
bool mMatrixOrPivotDirty;
- bool mCaching;
} mPrimitiveFields;
SkMatrix* mStaticMatrix;
SkMatrix* mAnimationMatrix;
+ LayerProperties mLayerProperties;
/**
* These fields are all generated from other properties and are not set directly.
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 2f714a1..30c6f5d 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -34,7 +34,7 @@
return a > b ? a : b;
}
-VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
+void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const Vector3& centroid3d, const Rect& casterBounds,
const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) {
@@ -57,16 +57,15 @@
#if DEBUG_SHADOW
ALOGD("Ambient shadow is out of clip rect!");
#endif
- return kVertexBufferMode_OnePolyRingShadow;
+ return;
}
- return AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
+ AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
casterVertexCount, centroid3d, heightFactor, geomFactor,
shadowVertexBuffer);
-
}
-VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
+void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
@@ -107,19 +106,17 @@
#if DEBUG_SHADOW
ALOGD("Spot shadow is out of clip rect!");
#endif
- return kVertexBufferMode_OnePolyRingShadow;
+ return;
}
- VertexBufferMode mode = SpotShadow::createSpotShadow(isCasterOpaque,
+ SpotShadow::createSpotShadow(isCasterOpaque,
casterPolygon, casterVertexCount, adjustedLightCenter, lightRadius,
lightVertexCount, shadowVertexBuffer);
-
#if DEBUG_SHADOW
if(shadowVertexBuffer.getVertexCount() <= 0) {
ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount());
}
#endif
- return mode;
}
void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) {
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index a1606ad..cb65df5 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -66,12 +66,12 @@
class ShadowTessellator {
public:
- static VertexBufferMode tessellateAmbientShadow(bool isCasterOpaque,
+ static void tessellateAmbientShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const Vector3& centroid3d, const Rect& casterBounds,
const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer);
- static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque,
+ static void tessellateSpotShadow(bool isCasterOpaque,
const Vector3* casterPolygon, int casterVertexCount,
const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius,
const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer);
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 3ebe7b4..06f6204 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -500,14 +500,14 @@
* empty strip if error.
*
*/
-VertexBufferMode SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly,
+void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& lightCenter, float lightSize,
int lightVertexCount, VertexBuffer& retStrips) {
Vector3 light[lightVertexCount * 3];
computeLightPolygon(lightVertexCount, lightCenter, lightSize, light);
computeSpotShadow(isCasterOpaque, light, lightVertexCount, lightCenter, poly,
polyLength, retStrips);
- return kVertexBufferMode_TwoPolyRingShadow;
+ retStrips.setMode(VertexBuffer::kTwoPolyRingShadow);
}
/**
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index fb3e6d5..d65ea89 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -26,7 +26,7 @@
class SpotShadow {
public:
- static VertexBufferMode createSpotShadow(bool isCasterOpaque, const Vector3* poly,
+ static void createSpotShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& lightCenter, float lightSize,
int lightVertexCount, VertexBuffer& retStrips);
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index fae25a6..95c0ee5 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -101,6 +101,10 @@
// Matrix
///////////////////////////////////////////////////////////////////////////////
+void StatefulBaseRenderer::getMatrix(Matrix4* matrix) const {
+ matrix->load(*(mSnapshot->transform));
+}
+
void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const {
mSnapshot->transform->copyTo(*matrix);
}
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index f38c752..e8e024f 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -69,6 +69,7 @@
// int alpha, SkXfermode::Mode mode, int flags);
// Matrix
+ void getMatrix(Matrix4* outMatrix) const;
virtual void getMatrix(SkMatrix* outMatrix) const;
virtual void translate(float dx, float dy, float dz = 0.0f);
virtual void rotate(float degrees);
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
new file mode 100644
index 0000000..ef3d0d7
--- /dev/null
+++ b/libs/hwui/TessellationCache.cpp
@@ -0,0 +1,485 @@
+/*
+ * 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"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <utils/JenkinsHash.h>
+#include <utils/Trace.h>
+
+#include "Caches.h"
+#include "OpenGLRenderer.h"
+#include "PathTessellator.h"
+#include "ShadowTessellator.h"
+#include "TessellationCache.h"
+
+#include "thread/Signal.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache entries
+///////////////////////////////////////////////////////////////////////////////
+
+TessellationCache::Description::Description()
+ : type(kNone)
+ , aa(false)
+ , cap(SkPaint::kDefault_Cap)
+ , style(SkPaint::kFill_Style)
+ , strokeWidth(1.0f) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+TessellationCache::Description::Description(Type type)
+ : type(type)
+ , aa(false)
+ , cap(SkPaint::kDefault_Cap)
+ , style(SkPaint::kFill_Style)
+ , strokeWidth(1.0f) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+TessellationCache::Description::Description(Type type, const SkPaint* paint)
+ : type(type)
+ , aa(paint->isAntiAlias())
+ , cap(paint->getStrokeCap())
+ , style(paint->getStyle())
+ , strokeWidth(paint->getStrokeWidth()) {
+ memset(&shape, 0, sizeof(Shape));
+}
+
+hash_t TessellationCache::Description::hash() const {
+ uint32_t hash = JenkinsHashMix(0, type);
+ hash = JenkinsHashMix(hash, aa);
+ hash = JenkinsHashMix(hash, cap);
+ hash = JenkinsHashMix(hash, style);
+ hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+ hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
+ return JenkinsHashWhiten(hash);
+}
+
+TessellationCache::ShadowDescription::ShadowDescription()
+ : nodeKey(NULL) {
+ memset(&matrixData, 0, 16 * sizeof(float));
+}
+
+TessellationCache::ShadowDescription::ShadowDescription(const void* nodeKey, const Matrix4* drawTransform)
+ : nodeKey(nodeKey) {
+ memcpy(&matrixData, drawTransform->data, 16 * sizeof(float));
+}
+
+hash_t TessellationCache::ShadowDescription::hash() const {
+ uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*) &nodeKey, sizeof(const void*));
+ hash = JenkinsHashMixBytes(hash, (uint8_t*) &matrixData, 16 * sizeof(float));
+ return JenkinsHashWhiten(hash);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// General purpose tessellation task processing
+///////////////////////////////////////////////////////////////////////////////
+
+class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
+public:
+ TessellationTask(Tessellator tessellator, const Description& description,
+ const SkPaint* paint)
+ : tessellator(tessellator)
+ , description(description)
+ , paint(*paint) {
+ }
+
+ ~TessellationTask() {}
+
+ Tessellator tessellator;
+ Description description;
+
+ //copied, since input paint may not be immutable
+ const SkPaint paint;
+};
+
+class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
+public:
+ TessellationProcessor(Caches& caches)
+ : TaskProcessor<VertexBuffer*>(&caches.tasks) {}
+ ~TessellationProcessor() {}
+
+ virtual void onProcess(const sp<Task<VertexBuffer*> >& task) {
+ TessellationTask* t = static_cast<TessellationTask*>(task.get());
+ ATRACE_NAME("shape tessellation");
+ VertexBuffer* buffer = t->tessellator(t->description, t->paint);
+ t->setResult(buffer);
+ }
+};
+
+struct TessellationCache::Buffer {
+public:
+ Buffer(const sp<Task<VertexBuffer*> >& task)
+ : mTask(task)
+ , mBuffer(NULL) {
+ }
+
+ ~Buffer() {
+ mTask.clear();
+ delete mBuffer;
+ }
+
+ unsigned int getSize() {
+ blockOnPrecache();
+ return mBuffer->getSize();
+ }
+
+ const VertexBuffer* getVertexBuffer() {
+ blockOnPrecache();
+ return mBuffer;
+ }
+
+private:
+ void blockOnPrecache() {
+ if (mTask != NULL) {
+ mBuffer = mTask->getResult();
+ LOG_ALWAYS_FATAL_IF(mBuffer == NULL, "Failed to precache");
+ mTask.clear();
+ }
+ }
+ sp<Task<VertexBuffer*> > mTask;
+ VertexBuffer* mBuffer;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Shadow tessellation task processing
+///////////////////////////////////////////////////////////////////////////////
+
+class ShadowTask : public Task<TessellationCache::vertexBuffer_pair_t*> {
+public:
+ ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque,
+ const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius)
+ : drawTransform(*drawTransform)
+ , localClip(localClip)
+ , opaque(opaque)
+ , casterPerimeter(*casterPerimeter)
+ , transformXY(*transformXY)
+ , transformZ(*transformZ)
+ , lightCenter(lightCenter)
+ , lightRadius(lightRadius) {
+ }
+
+ ~ShadowTask() {
+ TessellationCache::vertexBuffer_pair_t* bufferPair = getResult();
+ delete bufferPair->getFirst();
+ delete bufferPair->getSecond();
+ delete bufferPair;
+ }
+
+ /* Note - we deep copy all task parameters, because *even though* pointers into Allocator
+ * controlled objects (like the SkPath and Matrix4s) should be safe for the entire frame,
+ * certain Allocators are destroyed before trim() is called to flush incomplete tasks.
+ *
+ * These deep copies could be avoided, long term, by cancelling or flushing outstanding tasks
+ * before tearning down single-frame LinearAllocators.
+ */
+ const Matrix4 drawTransform;
+ const Rect localClip;
+ bool opaque;
+ const SkPath casterPerimeter;
+ const Matrix4 transformXY;
+ const Matrix4 transformZ;
+ const Vector3 lightCenter;
+ const float lightRadius;
+};
+
+static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) {
+ // map z coordinate with true 3d matrix
+ point.z = transformZ->mapZ(point);
+
+ // map x,y coordinates with draw/Skia matrix
+ transformXY->mapPoint(point.x, point.y);
+}
+
+static void tessellateShadows(
+ const Matrix4* drawTransform, const Rect* localClip,
+ bool isCasterOpaque, const SkPath* casterPerimeter,
+ const Matrix4* casterTransformXY, const Matrix4* casterTransformZ,
+ const Vector3& lightCenter, float lightRadius,
+ VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
+
+ // tessellate caster outline into a 2d polygon
+ Vector<Vertex> casterVertices2d;
+ const float casterRefinementThresholdSquared = 20.0f; // TODO: experiment with this value
+ PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
+ casterRefinementThresholdSquared, casterVertices2d);
+ if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
+ ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
+ casterVertices2d.size());
+ }
+
+ if (casterVertices2d.size() == 0) return;
+
+ // map 2d caster poly into 3d
+ const int casterVertexCount = casterVertices2d.size();
+ Vector3 casterPolygon[casterVertexCount];
+ float minZ = FLT_MAX;
+ float maxZ = -FLT_MAX;
+ for (int i = 0; i < casterVertexCount; i++) {
+ const Vertex& point2d = casterVertices2d[i];
+ casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
+ mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
+ minZ = fmin(minZ, casterPolygon[i].z);
+ maxZ = fmax(maxZ, casterPolygon[i].z);
+ }
+
+ // map the centroid of the caster into 3d
+ Vector2 centroid = ShadowTessellator::centroid2d(
+ reinterpret_cast<const Vector2*>(casterVertices2d.array()),
+ casterVertexCount);
+ Vector3 centroid3d(centroid.x, centroid.y, 0);
+ mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
+
+ // if the caster intersects the z=0 plane, lift it in Z so it doesn't
+ if (minZ < SHADOW_MIN_CASTER_Z) {
+ float casterLift = SHADOW_MIN_CASTER_Z - minZ;
+ for (int i = 0; i < casterVertexCount; i++) {
+ casterPolygon[i].z += casterLift;
+ }
+ centroid3d.z += casterLift;
+ }
+
+ // Check whether we want to draw the shadow at all by checking the caster's bounds against clip.
+ // We only have ortho projection, so we can just ignore the Z in caster for
+ // simple rejection calculation.
+ Rect casterBounds(casterPerimeter->getBounds());
+ casterTransformXY->mapRect(casterBounds);
+
+ // actual tessellation of both shadows
+ ShadowTessellator::tessellateAmbientShadow(
+ isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
+ casterBounds, *localClip, maxZ, ambientBuffer);
+
+ ShadowTessellator::tessellateSpotShadow(
+ isCasterOpaque, casterPolygon, casterVertexCount,
+ *drawTransform, lightCenter, lightRadius, casterBounds, *localClip,
+ spotBuffer);
+
+ // TODO: set ambientBuffer & spotBuffer's bounds for correct layer damage
+}
+
+class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t*> {
+public:
+ ShadowProcessor(Caches& caches)
+ : TaskProcessor<TessellationCache::vertexBuffer_pair_t*>(&caches.tasks) {}
+ ~ShadowProcessor() {}
+
+ virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t*> >& task) {
+ ShadowTask* t = static_cast<ShadowTask*>(task.get());
+ ATRACE_NAME("shadow tessellation");
+
+ VertexBuffer* ambientBuffer = new VertexBuffer;
+ VertexBuffer* spotBuffer = new VertexBuffer;
+ tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter,
+ &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius,
+ *ambientBuffer, *spotBuffer);
+
+ t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer));
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache constructor/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+TessellationCache::TessellationCache()
+ : mSize(0)
+ , mMaxSize(MB(DEFAULT_VERTEX_CACHE_SIZE))
+ , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
+ , mShadowCache(LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, NULL) > 0) {
+ INIT_LOGD(" Setting %s cache size to %sMB", name, property);
+ setMaxSize(MB(atof(property)));
+ } else {
+ INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_VERTEX_CACHE_SIZE);
+ }
+
+ mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
+ mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
+ mDebugEnabled = readDebugLevel() & kDebugCaches;
+}
+
+TessellationCache::~TessellationCache() {
+ mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t TessellationCache::getSize() {
+ LruCache<Description, Buffer*>::Iterator iter(mCache);
+ uint32_t size = 0;
+ while (iter.next()) {
+ size += iter.value()->getSize();
+ }
+ return size;
+}
+
+uint32_t TessellationCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void TessellationCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+
+void TessellationCache::trim() {
+ uint32_t size = getSize();
+ while (size > mMaxSize) {
+ size -= mCache.peekOldestValue()->getSize();
+ mCache.removeOldest();
+ }
+ mShadowCache.clear();
+}
+
+void TessellationCache::clear() {
+ mCache.clear();
+ mShadowCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void TessellationCache::BufferRemovedListener::operator()(Description& description,
+ Buffer*& buffer) {
+ delete buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Shadows
+///////////////////////////////////////////////////////////////////////////////
+
+void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius) {
+ ShadowDescription key(casterPerimeter, drawTransform);
+
+ sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
+ casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
+ if (mShadowProcessor == NULL) {
+ mShadowProcessor = new ShadowProcessor(Caches::getInstance());
+ }
+ mShadowProcessor->add(task);
+
+ task->incStrong(NULL); // not using sp<>s, so manually ref while in the cache
+ mShadowCache.put(key, task.get());
+}
+
+void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius, vertexBuffer_pair_t& outBuffers) {
+ ShadowDescription key(casterPerimeter, drawTransform);
+ ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
+ if (!task) {
+ precacheShadows(drawTransform, localClip, opaque, casterPerimeter,
+ transformXY, transformZ, lightCenter, lightRadius);
+ task = static_cast<ShadowTask*>(mShadowCache.get(key));
+ }
+ LOG_ALWAYS_FATAL_IF(task == NULL, "shadow not precached");
+ outBuffers = *(task->getResult());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Tessellation precaching
+///////////////////////////////////////////////////////////////////////////////
+
+static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint,
+ float scaleX, float scaleY) {
+ VertexBuffer* buffer = new VertexBuffer();
+ Matrix4 matrix;
+ matrix.loadScale(scaleX, scaleY, 1);
+ PathTessellator::tessellatePath(path, paint, matrix, *buffer);
+ return buffer;
+}
+
+TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
+ const Description& entry, Tessellator tessellator, const SkPaint* paint) {
+ Buffer* buffer = mCache.get(entry);
+ if (!buffer) {
+ // not cached, enqueue a task to fill the buffer
+ sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint);
+ buffer = new Buffer(task);
+
+ if (mProcessor == NULL) {
+ mProcessor = new TessellationProcessor(Caches::getInstance());
+ }
+ mProcessor->add(task);
+ mCache.put(entry, buffer);
+ }
+ return buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Rounded rects
+///////////////////////////////////////////////////////////////////////////////
+
+static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description,
+ const SkPaint& paint) {
+ SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth,
+ description.shape.roundRect.mHeight);
+ float rx = description.shape.roundRect.mRx;
+ float ry = description.shape.roundRect.mRy;
+ if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
+ float outset = paint.getStrokeWidth() / 2;
+ rect.outset(outset, outset);
+ rx += outset;
+ ry += outset;
+ }
+ SkPath path;
+ path.addRoundRect(rect, rx, ry);
+ return tessellatePath(path, &paint,
+ description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY);
+}
+
+TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint) {
+ Description entry(Description::kRoundRect, paint);
+ entry.shape.roundRect.mWidth = width;
+ entry.shape.roundRect.mHeight = height;
+ entry.shape.roundRect.mRx = rx;
+ entry.shape.roundRect.mRy = ry;
+ PathTessellator::extractTessellationScales(transform,
+ &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY);
+
+ return getOrCreateBuffer(entry, &tessellateRoundRect, paint);
+}
+const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint) {
+ return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
new file mode 100644
index 0000000..d4ff943
--- /dev/null
+++ b/libs/hwui/TessellationCache.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_TESSELLATION_CACHE_H
+#define ANDROID_HWUI_TESSELLATION_CACHE_H
+
+#include <utils/LruCache.h>
+#include <utils/Mutex.h>
+#include <utils/Vector.h>
+
+#include "Debug.h"
+#include "utils/Macros.h"
+#include "utils/Pair.h"
+#include "VertexBuffer.h"
+
+class SkBitmap;
+class SkCanvas;
+class SkPaint;
+class SkPath;
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
+class TessellationCache {
+public:
+ typedef Pair<VertexBuffer*, VertexBuffer*> vertexBuffer_pair_t;
+
+ struct Description {
+ DESCRIPTION_TYPE(Description);
+ enum Type {
+ kNone,
+ kRoundRect,
+ kAmbientShadow,
+ kSpotShadow
+ };
+
+ Type type;
+ bool aa;
+ SkPaint::Cap cap;
+ SkPaint::Style style;
+ float strokeWidth;
+ union Shape {
+ struct RoundRect {
+ float mScaleX;
+ float mScaleY;
+ float mWidth;
+ float mHeight;
+ float mRx;
+ float mRy;
+ } roundRect;
+ } shape;
+
+ Description();
+ Description(Type type);
+ Description(Type type, const SkPaint* paint);
+ hash_t hash() const;
+ };
+
+ struct ShadowDescription {
+ DESCRIPTION_TYPE(ShadowDescription);
+ const void* nodeKey;
+ float matrixData[16];
+
+ ShadowDescription();
+ ShadowDescription(const void* nodeKey, const Matrix4* drawTransform);
+ hash_t hash() const;
+ };
+
+ TessellationCache();
+ ~TessellationCache();
+
+ /**
+ * Clears the cache. This causes all TessellationBuffers to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+ /**
+ * Trims the contents of the cache, removing items until it's under its
+ * specified limit.
+ *
+ * Trimming is used for caches that support pre-caching from a worker
+ * thread. During pre-caching the maximum limit of the cache can be
+ * exceeded for the duration of the frame. It is therefore required to
+ * trim the cache at the end of the frame to keep the total amount of
+ * memory used under control.
+ *
+ * Also removes transient Shadow VertexBuffers, which aren't cached between frames.
+ */
+ void trim();
+
+ // TODO: precache/get for Oval, Lines, Points, etc.
+
+ void precacheRoundRect(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint) {
+ getRoundRectBuffer(transform, width, height, rx, ry, paint);
+ }
+ const VertexBuffer* getRoundRect(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint);
+
+ void precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius);
+
+ void getShadowBuffers(const Matrix4* drawTransform, const Rect& localClip,
+ bool opaque, const SkPath* casterPerimeter,
+ const Matrix4* transformXY, const Matrix4* transformZ,
+ const Vector3& lightCenter, float lightRadius,
+ vertexBuffer_pair_t& outBuffers);
+
+private:
+ class Buffer;
+ class TessellationTask;
+ class TessellationProcessor;
+
+
+ typedef VertexBuffer* (*Tessellator)(const Description&, const SkPaint&);
+
+ Buffer* getRoundRectBuffer(const Matrix4& transform,
+ float width, float height, float rx, float ry, const SkPaint* paint);
+
+ Buffer* getOrCreateBuffer(const Description& entry,
+ Tessellator tessellator, const SkPaint* paint);
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+
+ bool mDebugEnabled;
+
+ mutable Mutex mLock;
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // General tessellation caching
+ ///////////////////////////////////////////////////////////////////////////////
+ sp<TaskProcessor<VertexBuffer*> > mProcessor;
+ LruCache<Description, Buffer*> mCache;
+ class BufferRemovedListener : public OnEntryRemoved<Description, Buffer*> {
+ void operator()(Description& description, Buffer*& buffer);
+ };
+ BufferRemovedListener mBufferRemovedListener;
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Shadow tessellation caching
+ ///////////////////////////////////////////////////////////////////////////////
+ sp<TaskProcessor<vertexBuffer_pair_t*> > mShadowProcessor;
+
+ // holds a pointer, and implicit strong ref to each shadow task of the frame
+ LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*> mShadowCache;
+ class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t*>*> {
+ void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) {
+ bufferPairTask->decStrong(NULL);
+ }
+ };
+ BufferPairRemovedListener mBufferPairRemovedListener;
+
+}; // class TessellationCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_PATH_CACHE_H
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 8355f83..0fc0cef 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -18,11 +18,15 @@
#include <utils/Timers.h>
+#include "DamageAccumulator.h"
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
class BaseRenderNodeAnimator;
class AnimationListener;
+class OpenGLRenderer;
class AnimationHook {
public:
@@ -31,21 +35,49 @@
~AnimationHook() {}
};
-struct TreeInfo {
- // The defaults here should be safe for everyone but DrawFrameTask to use as-is.
- TreeInfo()
- : frameTimeMs(0)
+// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
+class TreeInfo {
+ PREVENT_COPY_AND_ASSIGN(TreeInfo);
+public:
+ enum TraversalMode {
+ // The full monty - sync, push, run animators, etc... Used by DrawFrameTask
+ // May only be used if both the UI thread and RT thread are blocked on the
+ // prepare
+ MODE_FULL,
+ // Run only what can be done safely on RT thread. Currently this only means
+ // animators, but potentially things like SurfaceTexture updates
+ // could be handled by this as well if there are no listeners
+ MODE_RT_ONLY,
+ // The subtree is being detached. Maybe. If the RenderNode is present
+ // in both the old and new display list's children then it will get a
+ // MODE_MAYBE_DETACHING followed shortly by a MODE_FULL.
+ // Push any pending display list changes in case it is detached,
+ // but don't evaluate animators and such as if it isn't detached as a
+ // MODE_FULL will follow shortly.
+ MODE_MAYBE_DETACHING,
+ // TODO: TRIM_MEMORY?
+ };
+
+ explicit TreeInfo(TraversalMode mode)
+ : mode(mode)
+ , frameTimeMs(0)
, animationHook(NULL)
- , prepareTextures(false)
- , performStagingPush(true)
- , evaluateAnimations(false)
+ , prepareTextures(mode == MODE_FULL)
+ , damageAccumulator(NullDamageAccumulator::instance())
+ , renderer(0)
{}
+ const TraversalMode mode;
nsecs_t frameTimeMs;
AnimationHook* animationHook;
+ // TODO: Remove this? Currently this is used to signal to stop preparing
+ // textures if we run out of cache space.
bool prepareTextures;
- bool performStagingPush;
- bool evaluateAnimations;
+ // Must not be null
+ IDamageAccumulator* damageAccumulator;
+ // The renderer that will be drawing the next frame. Use this to push any
+ // layer updates or similar. May be NULL.
+ OpenGLRenderer* renderer;
struct Out {
Out()
diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h
index 8b6872e..55d566b 100644
--- a/libs/hwui/VertexBuffer.h
+++ b/libs/hwui/VertexBuffer.h
@@ -23,10 +23,18 @@
class VertexBuffer {
public:
- VertexBuffer():
- mBuffer(0),
- mVertexCount(0),
- mCleanupMethod(NULL)
+ enum Mode {
+ kStandard = 0,
+ kOnePolyRingShadow = 1,
+ kTwoPolyRingShadow = 2
+ };
+
+ VertexBuffer()
+ : mBuffer(0)
+ , mVertexCount(0)
+ , mByteCount(0)
+ , mMode(kStandard)
+ , mCleanupMethod(NULL)
{}
~VertexBuffer() {
@@ -37,7 +45,7 @@
This should be the only method used by the Tessellator. Subsequent calls to
alloc will allocate space within the first allocation (useful if you want to
eventually allocate multiple regions within a single VertexBuffer, such as
- with PathTessellator::tesselateLines())
+ with PathTessellator::tessellateLines())
*/
template <class TYPE>
TYPE* alloc(int vertexCount) {
@@ -52,6 +60,7 @@
return reallocBuffer;
}
mVertexCount = vertexCount;
+ mByteCount = mVertexCount * sizeof(TYPE);
mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
mCleanupMethod = &(cleanup<TYPE>);
@@ -71,7 +80,13 @@
}
const void* getBuffer() const { return mBuffer; }
+ const Rect& getBounds() const { return mBounds; }
unsigned int getVertexCount() const { return mVertexCount; }
+ unsigned int getSize() const { return mByteCount; }
+ Mode getMode() const { return mMode; }
+
+ void setBounds(Rect bounds) { mBounds = bounds; }
+ void setMode(Mode mode) { mMode = mode; }
template <class TYPE>
void createDegenerateSeparators(int allocSize) {
@@ -88,8 +103,11 @@
delete[] (TYPE*)buffer;
}
+ Rect mBounds;
void* mBuffer;
unsigned int mVertexCount;
+ unsigned int mByteCount;
+ Mode mMode;
void* mReallocBuffer; // used for multi-allocation
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9ebee1d..440f965 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -439,6 +439,8 @@
mRenderThread.removeFrameCallback(this);
info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
+ info.damageAccumulator = &mDamageAccumulator;
+ info.renderer = mCanvas;
mRootRenderNode->prepareTree(info);
int runningBehind = 0;
@@ -465,27 +467,30 @@
mRenderThread.pushBackFrameCallback(this);
}
-void CanvasContext::draw(Rect* dirty) {
+void CanvasContext::draw() {
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
"drawDisplayList called on a context with no canvas or surface!");
profiler().markPlaybackStart();
+ SkRect dirty;
+ mDamageAccumulator.finish(&dirty);
+
EGLint width, height;
mGlobalContext->beginFrame(mEglSurface, &width, &height);
if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
mCanvas->setViewport(width, height);
- dirty = NULL;
+ dirty.setEmpty();
} else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
- dirty = NULL;
+ dirty.setEmpty();
} else {
- profiler().unionDirty(dirty);
+ profiler().unionDirty(&dirty);
}
status_t status;
- if (dirty && !dirty->isEmpty()) {
- status = mCanvas->prepareDirty(dirty->left, dirty->top,
- dirty->right, dirty->bottom, mOpaque);
+ if (!dirty.isEmpty()) {
+ status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
+ dirty.fRight, dirty.fBottom, mOpaque);
} else {
status = mCanvas->prepare(mOpaque);
}
@@ -516,14 +521,12 @@
profiler().startFrame();
- TreeInfo info;
- info.evaluateAnimations = true;
- info.performStagingPush = false;
+ TreeInfo info(TreeInfo::MODE_RT_ONLY);
info.prepareTextures = false;
prepareTree(info);
if (info.out.canDrawThisFrame) {
- draw(NULL);
+ draw();
}
}
@@ -543,7 +546,7 @@
bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
requireGlContext();
- TreeInfo info;
+ TreeInfo info(TreeInfo::MODE_FULL);
layer->apply(info);
return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 00c5bf0..d926b38 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -23,6 +23,7 @@
#include <utils/Functor.h>
#include <utils/Vector.h>
+#include "../DamageAccumulator.h"
#include "../DrawProfiler.h"
#include "../RenderNode.h"
#include "RenderTask.h"
@@ -57,7 +58,7 @@
void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info);
void prepareTree(TreeInfo& info);
- void draw(Rect* dirty);
+ void draw();
void destroyCanvasAndSurface();
// IFrameCallback, Chroreographer-driven frame callback entry point
@@ -99,6 +100,7 @@
bool mOpaque;
OpenGLRenderer* mCanvas;
bool mHaveNewSurface;
+ DamageAccumulator mDamageAccumulator;
const sp<RenderNode> mRootRenderNode;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 61d67ca..797566f 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -68,10 +68,6 @@
}
}
-void DrawFrameTask::setDirty(int left, int top, int right, int bottom) {
- mDirty.set(left, top, right, bottom);
-}
-
int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
@@ -83,7 +79,6 @@
// Reset the single-frame data
mFrameTimeNanos = 0;
mRecordDurationNanos = 0;
- mDirty.setEmpty();
return mSyncResult;
}
@@ -103,13 +98,12 @@
bool canUnblockUiThread;
bool canDrawThisFrame;
{
- TreeInfo info;
+ TreeInfo info(TreeInfo::MODE_FULL);
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
// Grab a copy of everything we need
- Rect dirty(mDirty);
CanvasContext* context = mContext;
// From this point on anything in "this" is *UNSAFE TO ACCESS*
@@ -118,7 +112,7 @@
}
if (CC_LIKELY(canDrawThisFrame)) {
- context->draw(&dirty);
+ context->draw();
}
if (!canUnblockUiThread) {
@@ -126,31 +120,19 @@
}
}
-static void initTreeInfo(TreeInfo& info) {
- info.prepareTextures = true;
- info.performStagingPush = true;
- info.evaluateAnimations = true;
-}
-
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
- initTreeInfo(info);
for (size_t i = 0; i < mLayers.size(); i++) {
mContext->processLayerUpdate(mLayers[i].get(), info);
}
mLayers.clear();
- if (info.out.hasAnimations) {
- // TODO: Uh... crap?
- }
mContext->prepareTree(info);
if (info.out.hasAnimations) {
- // TODO: dirty calculations, for now just do a full-screen inval
- mDirty.setEmpty();
if (info.out.requiresUiRedraw) {
mSyncResult |= kSync_UIRedrawRequired;
}
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index d4129b6..96f0add 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -60,7 +60,6 @@
void pushLayerUpdate(DeferredLayerUpdater* layer);
void removeLayerUpdate(DeferredLayerUpdater* layer);
- void setDirty(int left, int top, int right, int bottom);
void setDensity(float density) { mDensity = density; }
int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos);
@@ -80,7 +79,6 @@
/*********************************************
* Single frame data
*********************************************/
- Rect mDirty;
nsecs_t mFrameTimeNanos;
nsecs_t mRecordDurationNanos;
float mDensity;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 0901963..4988f19 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -47,7 +47,7 @@
#define SETUP_TASK(method) \
LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
- "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \
+ "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \
METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \
MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
ARGS(method) *args = (ARGS(method) *) task->payload()
@@ -180,8 +180,7 @@
}
int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
- float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
- mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+ float density) {
mDrawFrameTask.setDensity(density);
return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 944ff9c..a95f8f0 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -70,7 +70,7 @@
ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
ANDROID_API void setOpaque(bool opaque);
ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
- float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+ float density);
ANDROID_API void destroyCanvasAndSurface();
ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion);
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 189895c..3d2b0d9 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -16,9 +16,10 @@
#include <sys/sysinfo.h>
+#include "TaskManager.h"
#include "Task.h"
#include "TaskProcessor.h"
-#include "TaskManager.h"
+#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -31,7 +32,8 @@
// Get the number of available CPUs. This value does not change over time.
int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
- for (int i = 0; i < cpuCount / 2; i++) {
+ int workerCount = MathUtils::max(1, cpuCount / 2);
+ for (int i = 0; i < workerCount; i++) {
String8 name;
name.appendFormat("hwuiTask%d", i + 1);
mThreads.add(new WorkerThread(name));
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 14a3ec0..5b7c87c 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -21,5 +21,12 @@
Type(const Type&); \
void operator=(const Type&)
+#define DESCRIPTION_TYPE(Type) \
+ int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \
+ bool operator==(const Type& other) const { return compare(other) == 0; } \
+ bool operator!=(const Type& other) const { return compare(other) != 0; } \
+ friend inline int strictly_order_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs) < 0; } \
+ friend inline int compare_type(const Type& lhs, const Type& rhs) { return lhs.compare(rhs); } \
+ friend inline hash_t hash_type(const Type& entry) { return entry.hash(); }
#endif /* MACROS_H */
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 997acde2..65f1663 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -38,6 +38,10 @@
return isZero(valueA - valueB);
}
+ inline static int max(int a, int b) {
+ return a > b ? a : b;
+ }
+
inline static int min(int a, int b) {
return a < b ? a : b;
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bb23a36..394e437 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -17,6 +17,8 @@
package android.media;
import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -29,7 +31,7 @@
* A class to encapsulate a collection of attributes describing information about an audio
* player or recorder.
*/
-public final class AudioAttributes {
+public final class AudioAttributes implements Parcelable {
private final static String TAG = "AudioAttributes";
/**
@@ -192,6 +194,7 @@
}
/**
+ * @hide
* Return the set of tags.
* @return a read-only set of all tags stored as strings.
*/
@@ -324,6 +327,7 @@
}
/**
+ * @hide
* Add a custom tag stored as a string
* @param tag
* @return the same Builder instance.
@@ -410,6 +414,49 @@
+ " tags=" + mTags);
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mUsage);
+ dest.writeInt(mContentType);
+ dest.writeInt(mFlags);
+ String[] tagsArray = new String[mTags.size()];
+ mTags.toArray(tagsArray);
+ dest.writeStringArray(tagsArray);
+ }
+
+ private AudioAttributes(Parcel in) {
+ mUsage = in.readInt();
+ mContentType = in.readInt();
+ mFlags = in.readInt();
+ mTags = new HashSet<String>();
+ String[] tagsArray = in.readStringArray();
+ for (int i = tagsArray.length - 1 ; i >= 0 ; i--) {
+ mTags.add(tagsArray[i]);
+ }
+ }
+
+ /** @hide */
+ public static final Parcelable.Creator<AudioAttributes> CREATOR
+ = new Parcelable.Creator<AudioAttributes>() {
+ /**
+ * Rebuilds an AudioAttributes previously stored with writeToParcel().
+ * @param p Parcel object to read the AudioAttributes from
+ * @return a new AudioAttributes created from the data in the parcel
+ */
+ public AudioAttributes createFromParcel(Parcel p) {
+ return new AudioAttributes(p);
+ }
+ public AudioAttributes[] newArray(int size) {
+ return new AudioAttributes[size];
+ }
+ };
+
+
/** @hide */
@IntDef({
USAGE_UNKNOWN,
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b07d2c5..4b4be1b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -16,6 +16,11 @@
package android.media;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* The AudioFormat class is used to access a number of audio format and
* channel configuration constants. They are for instance used
@@ -162,4 +167,151 @@
throw new UnsupportedOperationException("There is no valid usage of this constructor");
}
+ /**
+ * Private constructor with an ignored argument to differentiate from the removed default ctor
+ * @param ignoredArgument
+ */
+ private AudioFormat(int ignoredArgument) {
+ }
+
+ /** @hide */
+ public final static int AUDIO_FORMAT_HAS_PROPERTY_NONE = 0x0;
+ /** @hide */
+ public final static int AUDIO_FORMAT_HAS_PROPERTY_ENCODING = 0x1 << 0;
+ /** @hide */
+ public final static int AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE = 0x1 << 1;
+ /** @hide */
+ public final static int AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK = 0x1 << 2;
+
+ private int mEncoding;
+ private int mSampleRate;
+ private int mChannelMask;
+ private int mPropertySetMask;
+
+ /**
+ * @hide CANDIDATE FOR PUBLIC API
+ * Builder class for {@link AudioFormat} objects.
+ */
+ public static class Builder {
+ private int mEncoding = ENCODING_DEFAULT;
+ private int mSampleRate = 0;
+ private int mChannelMask = CHANNEL_INVALID;
+ private int mPropertySetMask = AUDIO_FORMAT_HAS_PROPERTY_NONE;
+
+ /**
+ * Constructs a new Builder with the defaults.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a new Builder from a given {@link AudioFormat}.
+ * @param af the {@link AudioFormat} object whose data will be reused in the new Builder.
+ */
+ public Builder(AudioFormat af) {
+ mEncoding = af.mEncoding;
+ mSampleRate = af.mSampleRate;
+ mChannelMask = af.mChannelMask;
+ mPropertySetMask = af.mPropertySetMask;
+ }
+
+ /**
+ * Combines all of the format characteristics that have been set and return a new
+ * {@link AudioFormat} object.
+ * @return a new {@link AudioFormat} object
+ */
+ public AudioFormat build() {
+ AudioFormat af = new AudioFormat(1980/*ignored*/);
+ af.mEncoding = mEncoding;
+ af.mSampleRate = mSampleRate;
+ af.mChannelMask = mChannelMask;
+ af.mPropertySetMask = mPropertySetMask;
+ return af;
+ }
+
+ /**
+ * Sets the data encoding format.
+ * @param encoding one of {@link AudioFormat#ENCODING_DEFAULT},
+ * {@link AudioFormat#ENCODING_PCM_8BIT},
+ * {@link AudioFormat#ENCODING_PCM_16BIT},
+ * {@link AudioFormat#ENCODING_PCM_FLOAT}.
+ * @return the same Builder instance.
+ * @throws java.lang.IllegalArgumentException
+ */
+ public Builder setEncoding(@Encoding int encoding) throws IllegalArgumentException {
+ switch (encoding) {
+ case ENCODING_DEFAULT:
+ mEncoding = ENCODING_PCM_16BIT;
+ break;
+ case ENCODING_PCM_8BIT:
+ case ENCODING_PCM_16BIT:
+ case ENCODING_PCM_FLOAT:
+ mEncoding = encoding;
+ break;
+ case ENCODING_INVALID:
+ default:
+ throw new IllegalArgumentException("Invalid encoding " + encoding);
+ }
+ mPropertySetMask |= AUDIO_FORMAT_HAS_PROPERTY_ENCODING;
+ return this;
+ }
+
+ /**
+ * Sets the channel mask.
+ * @param channelMask describes the configuration of the audio channels.
+ * <p>For output, the mask should be a combination of
+ * {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT},
+ * {@link AudioFormat#CHANNEL_OUT_FRONT_CENTER},
+ * {@link AudioFormat#CHANNEL_OUT_FRONT_RIGHT},
+ * {@link AudioFormat#CHANNEL_OUT_SIDE_LEFT},
+ * {@link AudioFormat#CHANNEL_OUT_SIDE_RIGHT},
+ * {@link AudioFormat#CHANNEL_OUT_BACK_LEFT},
+ * {@link AudioFormat#CHANNEL_OUT_BACK_RIGHT}.
+ * <p>for input, the mask should be {@link AudioFormat#CHANNEL_IN_MONO} or
+ * {@link AudioFormat#CHANNEL_IN_STEREO}. {@link AudioFormat#CHANNEL_IN_MONO} is
+ * guaranteed to work on all devices.
+ * @return the same Builder instance.
+ */
+ public Builder setChannelMask(int channelMask) {
+ // only validated when used, with input or output context
+ mChannelMask = channelMask;
+ mPropertySetMask |= AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK;
+ return this;
+ }
+
+ /**
+ * Sets the sample rate.
+ * @param sampleRate the sample rate expressed in Hz
+ * @return the same Builder instance.
+ * @throws java.lang.IllegalArgumentException
+ */
+ public Builder setSampleRate(int sampleRate) throws IllegalArgumentException {
+ if ((sampleRate <= 0) || (sampleRate > 192000)) {
+ throw new IllegalArgumentException("Invalid sample rate " + sampleRate);
+ }
+ mSampleRate = sampleRate;
+ mPropertySetMask |= AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE;
+ return this;
+ }
+ }
+
+ @Override
+ public String toString () {
+ return new String("AudioFormat:"
+ + " props=" + mPropertySetMask
+ + " enc=" + mEncoding
+ + " chan=0x" + Integer.toHexString(mChannelMask)
+ + " rate=" + mSampleRate);
+ }
+
+ /** @hide */
+ @IntDef({
+ ENCODING_DEFAULT,
+ ENCODING_PCM_8BIT,
+ ENCODING_PCM_16BIT,
+ ENCODING_PCM_FLOAT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Encoding {}
+
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 84d4ab6..6280fde 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1017,7 +1017,7 @@
public void setMasterMute(boolean state, int flags) {
IAudioService service = getService();
try {
- service.setMasterMute(state, flags, mICallBack);
+ service.setMasterMute(state, flags, mContext.getOpPackageName(), mICallBack);
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setMasterMute", e);
}
@@ -1653,6 +1653,25 @@
}
}
+
+ /**
+ * Return a new audio session identifier not associated with any player or effect.
+ * It can for instance be used to create one of the {@link android.media.audiofx.AudioEffect}
+ * objects.
+ * @return a new unclaimed and unused audio session identifier, or {@link #ERROR} when the
+ * system failed to allocate a new session.
+ */
+ public int allocateAudioSessionId() {
+ int session = AudioSystem.newAudioSessionId();
+ if (session > 0) {
+ return session;
+ } else {
+ Log.e(TAG, "Failure to allocate a new audio session ID");
+ return ERROR;
+ }
+ }
+
+
/*
* Sets a generic audio configuration parameter. The use of these parameters
* are platform dependant, see libaudio
@@ -2344,19 +2363,24 @@
if (rctlr == null) {
return false;
}
- IAudioService service = getService();
- final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener();
- final ComponentName listenerComponent = new ComponentName(mContext, l.getClass());
- try {
- int[] artworkDimensions = rctlr.getArtworkSize();
- boolean reg = service.registerRemoteController(rctlr.getRcDisplay(),
- artworkDimensions[0]/*w*/, artworkDimensions[1]/*h*/,
- listenerComponent);
- rctlr.setIsRegistered(reg);
- return reg;
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerRemoteController " + e);
- return false;
+ if (USE_SESSIONS) {
+ rctlr.startListeningToSessions();
+ return true;
+ } else {
+ IAudioService service = getService();
+ final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener();
+ final ComponentName listenerComponent = new ComponentName(mContext, l.getClass());
+ try {
+ int[] artworkDimensions = rctlr.getArtworkSize();
+ boolean reg = service.registerRemoteController(rctlr.getRcDisplay(),
+ artworkDimensions[0]/* w */, artworkDimensions[1]/* h */,
+ listenerComponent);
+ rctlr.setIsRegistered(reg);
+ return reg;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in registerRemoteController " + e);
+ return false;
+ }
}
}
@@ -2369,12 +2393,16 @@
if (rctlr == null) {
return;
}
- IAudioService service = getService();
- try {
- service.unregisterRemoteControlDisplay(rctlr.getRcDisplay());
- rctlr.setIsRegistered(false);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
+ if (USE_SESSIONS) {
+ rctlr.stopListeningToSessions();
+ } else {
+ IAudioService service = getService();
+ try {
+ service.unregisterRemoteControlDisplay(rctlr.getRcDisplay());
+ rctlr.setIsRegistered(false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
+ }
}
}
@@ -2998,7 +3026,8 @@
/** @hide
*/
public static final int SUCCESS = AudioSystem.SUCCESS;
- /** @hide
+ /**
+ * A default error code.
*/
public static final int ERROR = AudioSystem.ERROR;
/** @hide
@@ -3188,15 +3217,11 @@
do {
newPorts.clear();
status = AudioSystem.listAudioPorts(newPorts, portGeneration);
- Log.i(TAG, "updateAudioPortCache AudioSystem.listAudioPorts() status: "+
- status+" num ports: "+ newPorts.size() +" portGeneration: "+portGeneration[0]);
if (status != SUCCESS) {
return status;
}
newPatches.clear();
status = AudioSystem.listAudioPatches(newPatches, patchGeneration);
- Log.i(TAG, "updateAudioPortCache AudioSystem.listAudioPatches() status: "+
- status+" num patches: "+ newPatches.size() +" patchGeneration: "+patchGeneration[0]);
if (status != SUCCESS) {
return status;
}
@@ -3204,14 +3229,16 @@
for (int i = 0; i < newPatches.size(); i++) {
for (int j = 0; j < newPatches.get(i).sources().length; j++) {
- AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j], newPorts);
+ AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j],
+ newPorts);
if (portCfg == null) {
return ERROR;
}
newPatches.get(i).sources()[j] = portCfg;
}
for (int j = 0; j < newPatches.get(i).sinks().length; j++) {
- AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j], newPorts);
+ AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j],
+ newPorts);
if (portCfg == null) {
return ERROR;
}
@@ -3242,8 +3269,6 @@
// compare handles because the port returned by JNI is not of the correct
// subclass
if (ports.get(k).handle().equals(port.handle())) {
- Log.i(TAG, "updatePortConfig match found for port handle: "+
- port.handle().id()+" port: "+ k);
port = ports.get(k);
break;
}
diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java
index fbd5022..8b74842 100644
--- a/media/java/android/media/AudioPort.java
+++ b/media/java/android/media/AudioPort.java
@@ -133,7 +133,7 @@
* Get the gain descriptor at a given index
*/
AudioGain gain(int index) {
- if (index < mGains.length) {
+ if (index < 0 || index >= mGains.length) {
return null;
}
return mGains[index];
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index 782ecd8..d5fea07 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -56,7 +56,6 @@
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
- Log.i(TAG, "handleMessage: "+msg.what);
ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
synchronized (this) {
if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 74f39b7..2f782cc 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -1321,11 +1321,16 @@
}
/** @see AudioManager#setMasterMute(boolean, int) */
- public void setMasterMute(boolean state, int flags, IBinder cb) {
+ public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
if (mUseFixedVolume) {
return;
}
+ if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ return;
+ }
+
if (state != AudioSystem.getMasterMute()) {
AudioSystem.setMasterMute(state);
// Post a persist master volume msg
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index c8d64ce..9fbcd18 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -130,6 +130,11 @@
public static native boolean isSourceActive(int source);
/*
+ * Returns a new unused audio session ID
+ */
+ public static native int newAudioSessionId();
+
+ /*
* Sets a group generic audio configuration parameters. The use of these parameters
* are platform dependent, see libaudio
*
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 8eb83e4..cfd9c3b 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -816,6 +816,8 @@
* with the estimated time when that frame was presented or is committed to
* be presented.
* In the case that no timestamp is available, any supplied instance is left unaltered.
+ * A timestamp may be temporarily unavailable while the audio clock is stabilizing,
+ * or during and immediately after a route change.
*/
// Add this text when the "on new timestamp" API is added:
// Use if you need to get the most recent timestamp outside of the event callback handler.
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index 511111c..f9e49c1 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -90,9 +90,14 @@
*/
public static final int QUALITY_QVGA = 7;
+ /**
+ * Quality level corresponding to the 2160p (3840x2160) resolution.
+ */
+ public static final int QUALITY_2160P = 8;
+
// Start and end of quality list
private static final int QUALITY_LIST_START = QUALITY_LOW;
- private static final int QUALITY_LIST_END = QUALITY_QVGA;
+ private static final int QUALITY_LIST_END = QUALITY_2160P;
/**
* Time lapse quality level corresponding to the lowest available resolution.
@@ -134,9 +139,14 @@
*/
public static final int QUALITY_TIME_LAPSE_QVGA = 1007;
+ /**
+ * Time lapse quality level corresponding to the 2160p (3840 x 2160) resolution.
+ */
+ public static final int QUALITY_TIME_LAPSE_2160P = 1008;
+
// Start and end of timelapse quality list
private static final int QUALITY_TIME_LAPSE_LIST_START = QUALITY_TIME_LAPSE_LOW;
- private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_QVGA;
+ private static final int QUALITY_TIME_LAPSE_LIST_END = QUALITY_TIME_LAPSE_2160P;
/**
* Default recording duration in seconds before the session is terminated.
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e59623b..ba3cfb6 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -62,7 +62,7 @@
boolean isStreamMute(int streamType);
- void setMasterMute(boolean state, int flags, IBinder cb);
+ void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
boolean isMasterMute();
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index a4d491d8..1da0215 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -28,6 +28,8 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.WifiDisplay;
import android.hardware.display.WifiDisplayStatus;
+import android.media.session.MediaSession;
+import android.media.session.RemoteVolumeProvider;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -58,6 +60,7 @@
public class MediaRouter {
private static final String TAG = "MediaRouter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean USE_SESSIONS = true;
static class Static implements DisplayManager.DisplayListener {
final Context mAppContext;
@@ -1141,7 +1144,7 @@
public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
}
-
+
/**
* Create a new route category. Each route must belong to a category.
*
@@ -1980,6 +1983,7 @@
*/
public static class UserRouteInfo extends RouteInfo {
RemoteControlClient mRcc;
+ SessionVolumeProvider mSvp;
UserRouteInfo(RouteCategory category) {
super(category);
@@ -1996,7 +2000,7 @@
mName = name;
routeUpdated();
}
-
+
/**
* Set the user-visible name of this route.
* <p>
@@ -2100,7 +2104,11 @@
public void setPlaybackType(int type) {
if (mPlaybackType != type) {
mPlaybackType = type;
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type);
+ }
}
}
@@ -2113,8 +2121,12 @@
public void setVolumeHandling(int volumeHandling) {
if (mVolumeHandling != volumeHandling) {
mVolumeHandling = volumeHandling;
- setPlaybackInfoOnRcc(
- RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ setPlaybackInfoOnRcc(
+ RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling);
+ }
}
}
@@ -2127,7 +2139,13 @@
volume = Math.max(0, Math.min(volume, getVolumeMax()));
if (mVolume != volume) {
mVolume = volume;
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume);
+ if (USE_SESSIONS) {
+ if (mSvp != null) {
+ mSvp.notifyVolumeChanged();
+ }
+ } else {
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume);
+ }
dispatchRouteVolumeChanged(this);
if (mGroup != null) {
mGroup.memberVolumeChanged(this);
@@ -2166,7 +2184,11 @@
public void setVolumeMax(int volumeMax) {
if (mVolumeMax != volumeMax) {
mVolumeMax = volumeMax;
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax);
+ }
}
}
@@ -2177,37 +2199,120 @@
public void setPlaybackStream(int stream) {
if (mPlaybackStream != stream) {
mPlaybackStream = stream;
- setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream);
+ }
}
}
private void updatePlaybackInfoOnRcc() {
- if ((mRcc != null) && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) {
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream);
- mRcc.setPlaybackInformation(
- RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType);
- // let AudioService know whom to call when remote volume needs to be updated
- try {
- sStatic.mAudioService.registerRemoteVolumeObserverForRcc(
- mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */);
- } catch (RemoteException e) {
- Log.e(TAG, "Error registering remote volume observer", e);
+ if (USE_SESSIONS) {
+ configureSessionVolume();
+ } else {
+ if ((mRcc != null)
+ && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) {
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax);
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume);
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling);
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream);
+ mRcc.setPlaybackInformation(
+ RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType);
+ // let AudioService know whom to call when remote volume
+ // needs to be updated
+ try {
+ sStatic.mAudioService.registerRemoteVolumeObserverForRcc(
+ mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error registering remote volume observer", e);
+ }
}
}
}
+ private void configureSessionVolume() {
+ if (mRcc == null) {
+ if (DEBUG) {
+ Log.d(TAG, "No Rcc to configure volume for route " + mName);
+ }
+ return;
+ }
+ MediaSession session = mRcc.getMediaSession();
+ if (session == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Rcc has no session to configure volume");
+ }
+ return;
+ }
+ if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
+ int volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_FIXED;
+ switch (mVolumeHandling) {
+ case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
+ volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ break;
+ case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
+ default:
+ break;
+ }
+ // Only register a new listener if necessary
+ if (mSvp == null || mSvp.getVolumeControl() != volumeControl
+ || mSvp.getMaxVolume() != mVolumeMax) {
+ mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax);
+ session.setPlaybackToRemote(mSvp);
+ }
+ } else {
+ // We only know how to handle local and remote, fall back to local if not remote.
+ session.setPlaybackToLocal(mPlaybackStream);
+ mSvp = null;
+ }
+ }
+
private void setPlaybackInfoOnRcc(int what, int value) {
if (mRcc != null) {
mRcc.setPlaybackInformation(what, value);
}
}
+
+ class SessionVolumeProvider extends RemoteVolumeProvider {
+
+ public SessionVolumeProvider(int volumeControl, int maxVolume) {
+ super(volumeControl, maxVolume);
+ }
+
+ @Override
+ public int onGetCurrentVolume() {
+ return mVcb == null ? 0 : mVcb.route.mVolume;
+ }
+
+ @Override
+ public void onSetVolumeTo(final int volume) {
+ sStatic.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mVcb != null) {
+ mVcb.vcb.onVolumeSetRequest(mVcb.route, volume);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onAdjustVolumeBy(final int delta) {
+ sStatic.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mVcb != null) {
+ mVcb.vcb.onVolumeUpdateRequest(mVcb.route, delta);
+ }
+ }
+ });
+ }
+ }
}
/**
@@ -2504,17 +2609,17 @@
public CharSequence getName() {
return getName(sStatic.mResources);
}
-
+
/**
* Return the properly localized/configuration dependent name of this RouteCategory.
- *
+ *
* @param context Context to resolve name resources
* @return the name of this route category
*/
public CharSequence getName(Context context) {
return getName(context.getResources());
}
-
+
CharSequence getName(Resources res) {
if (mNameResId != 0) {
return res.getText(mNameResId);
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 3711585..76c7299 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -19,11 +19,17 @@
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.IRemoteControlDisplay;
import android.media.MediaMetadataEditor;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionLegacyHelper;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -34,6 +40,7 @@
import android.view.KeyEvent;
import java.lang.ref.WeakReference;
+import java.util.List;
/**
* The RemoteController class is used to control media playback, display and update media metadata
@@ -56,6 +63,7 @@
private final static int TRANSPORT_UNKNOWN = 0;
private final static String TAG = "RemoteController";
private final static boolean DEBUG = false;
+ private final static boolean USE_SESSIONS = true;
private final static Object mGenLock = new Object();
private final static Object mInfoLock = new Object();
private final RcDisplay mRcd;
@@ -64,6 +72,11 @@
private final int mMaxBitmapDimension;
private MetadataEditor mMetadataEditor;
+ private MediaSessionManager mSessionManager;
+ private MediaSessionManager.SessionListener mSessionListener
+ = new TopTransportSessionListener();
+ private MediaController.Callback mSessionCb = new MediaControllerCallback();
+
/**
* Synchronized on mGenLock
*/
@@ -79,6 +92,8 @@
private int mArtworkWidth = -1;
private int mArtworkHeight = -1;
private boolean mEnabled = true;
+ // synchronized on mInfoLock, for USE_SESSION apis.
+ private MediaController mCurrentSession;
/**
* Class constructor.
@@ -123,6 +138,8 @@
mContext = context;
mRcd = new RcDisplay(this);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mSessionManager = (MediaSessionManager) context
+ .getSystemService(Context.MEDIA_SESSION_SERVICE);
if (ActivityManager.isLowRamDeviceStatic()) {
mMaxBitmapDimension = MAX_BITMAP_DIMENSION;
@@ -194,8 +211,15 @@
* @hide
*/
public String getRemoteControlClientPackageName() {
- return mClientPendingIntentCurrent != null ?
- mClientPendingIntentCurrent.getCreatorPackage() : null;
+ if (USE_SESSIONS) {
+ synchronized (mInfoLock) {
+ return mCurrentSession != null ? mCurrentSession.getSessionInfo().getPackageName()
+ : null;
+ }
+ } else {
+ return mClientPendingIntentCurrent != null ?
+ mClientPendingIntentCurrent.getCreatorPackage() : null;
+ }
}
/**
@@ -215,22 +239,38 @@
* @see OnClientUpdateListener#onClientPlaybackStateUpdate(int, long, long, float)
*/
public long getEstimatedMediaPosition() {
- if (mLastPlaybackInfo != null) {
- if (!RemoteControlClient.playbackPositionShouldMove(mLastPlaybackInfo.mState)) {
- return mLastPlaybackInfo.mCurrentPosMs;
+ if (USE_SESSIONS) {
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ PlaybackState state = mCurrentSession.getPlaybackState();
+ if (state != null) {
+ return state.getPosition();
+ }
+ }
}
-
- // Take the current position at the time of state change and estimate.
- final long thenPos = mLastPlaybackInfo.mCurrentPosMs;
- if (thenPos < 0) {
- return -1;
+ } else {
+ final PlaybackInfo lastPlaybackInfo;
+ synchronized (mInfoLock) {
+ lastPlaybackInfo = mLastPlaybackInfo;
}
+ if (lastPlaybackInfo != null) {
+ if (!RemoteControlClient.playbackPositionShouldMove(lastPlaybackInfo.mState)) {
+ return lastPlaybackInfo.mCurrentPosMs;
+ }
- final long now = SystemClock.elapsedRealtime();
- final long then = mLastPlaybackInfo.mStateChangeTimeMs;
- final long sinceThen = now - then;
- final long scaledSinceThen = (long) (sinceThen * mLastPlaybackInfo.mSpeed);
- return thenPos + scaledSinceThen;
+ // Take the current position at the time of state change and
+ // estimate.
+ final long thenPos = lastPlaybackInfo.mCurrentPosMs;
+ if (thenPos < 0) {
+ return -1;
+ }
+
+ final long now = SystemClock.elapsedRealtime();
+ final long then = lastPlaybackInfo.mStateChangeTimeMs;
+ final long sinceThen = now - then;
+ final long scaledSinceThen = (long) (sinceThen * lastPlaybackInfo.mSpeed);
+ return thenPos + scaledSinceThen;
+ }
}
return -1;
}
@@ -267,30 +307,40 @@
if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
throw new IllegalArgumentException("not a media key event");
}
- final PendingIntent pi;
- synchronized(mInfoLock) {
- if (!mIsRegistered) {
- Log.e(TAG, "Cannot use sendMediaKeyEvent() from an unregistered RemoteController");
- return false;
- }
- if (!mEnabled) {
- Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController");
- return false;
- }
- pi = mClientPendingIntentCurrent;
- }
- if (pi != null) {
- Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- try {
- pi.send(mContext, 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending intent for media button down: ", e);
+ if (USE_SESSIONS) {
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ return mCurrentSession.dispatchMediaButtonEvent(keyEvent);
+ }
return false;
}
} else {
- Log.i(TAG, "No-op when sending key click, no receiver right now");
- return false;
+ final PendingIntent pi;
+ synchronized (mInfoLock) {
+ if (!mIsRegistered) {
+ Log.e(TAG,
+ "Cannot use sendMediaKeyEvent() from an unregistered RemoteController");
+ return false;
+ }
+ if (!mEnabled) {
+ Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController");
+ return false;
+ }
+ pi = mClientPendingIntentCurrent;
+ }
+ if (pi != null) {
+ Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+ intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ try {
+ pi.send(mContext, 0, intent);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Error sending intent for media button down: ", e);
+ return false;
+ }
+ } else {
+ Log.i(TAG, "No-op when sending key click, no receiver right now");
+ return false;
+ }
}
return true;
}
@@ -311,11 +361,19 @@
if (timeMs < 0) {
throw new IllegalArgumentException("illegal negative time value");
}
- final int genId;
- synchronized (mGenLock) {
- genId = mClientGenerationIdCurrent;
+ if (USE_SESSIONS) {
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ mCurrentSession.getTransportControls().seekTo(timeMs);
+ }
+ }
+ } else {
+ final int genId;
+ synchronized (mGenLock) {
+ genId = mClientGenerationIdCurrent;
+ }
+ mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
}
- mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
return true;
}
@@ -430,7 +488,6 @@
return editor;
}
-
/**
* A class to read the metadata published by a {@link RemoteControlClient}, or send a
* {@link RemoteControlClient} new values for keys that can be edited.
@@ -477,26 +534,41 @@
if (!mMetadataChanged) {
return;
}
- final int genId;
- synchronized(mGenLock) {
- genId = mClientGenerationIdCurrent;
- }
- synchronized(mInfoLock) {
- if (mEditorMetadata.containsKey(
- String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
- Rating rating = (Rating) getObject(
- MediaMetadataEditor.RATING_KEY_BY_USER, null);
- mAudioManager.updateRemoteControlClientMetadata(genId,
- MediaMetadataEditor.RATING_KEY_BY_USER,
- rating);
- } else {
- Log.e(TAG, "no metadata to apply");
+ if (USE_SESSIONS) {
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ if (mEditorMetadata.containsKey(
+ String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
+ Rating rating = (Rating) getObject(
+ MediaMetadataEditor.RATING_KEY_BY_USER, null);
+ if (rating != null) {
+ mCurrentSession.getTransportControls().setRating(rating);
+ }
+ }
+ }
}
- // NOT setting mApplied to true as this type of MetadataEditor will be applied
- // multiple times, whenever the user of a RemoteController needs to change the
- // metadata (e.g. user changes the rating of a song more than once during playback)
- mApplied = false;
+ } else {
+ final int genId;
+ synchronized(mGenLock) {
+ genId = mClientGenerationIdCurrent;
+ }
+ synchronized(mInfoLock) {
+ if (mEditorMetadata.containsKey(
+ String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
+ Rating rating = (Rating) getObject(
+ MediaMetadataEditor.RATING_KEY_BY_USER, null);
+ mAudioManager.updateRemoteControlClientMetadata(genId,
+ MediaMetadataEditor.RATING_KEY_BY_USER,
+ rating);
+ } else {
+ Log.e(TAG, "no metadata to apply");
+ }
+ }
}
+ // NOT setting mApplied to true as this type of MetadataEditor will be applied
+ // multiple times, whenever the user of a RemoteController needs to change the
+ // metadata (e.g. user changes the rating of a song more than once during playback)
+ mApplied = false;
}
}
@@ -649,6 +721,46 @@
}
}
+ /**
+ * This receives updates when the current session changes. This is
+ * registered to receive the updates on the handler thread so it can call
+ * directly into the appropriate methods.
+ */
+ private class MediaControllerCallback extends MediaController.Callback {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ onNewPlaybackState(state);
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ onNewMediaMetadata(metadata);
+ }
+ }
+
+ /**
+ * Listens for changes to the active session stack and replaces the
+ * currently tracked session if it has changed.
+ */
+ private class TopTransportSessionListener extends MediaSessionManager.SessionListener {
+ @Override
+ public void onActiveSessionsChanged(List<MediaController> controllers) {
+ int size = controllers.size();
+ for (int i = 0; i < size; i++) {
+ MediaController controller = controllers.get(i);
+ long flags = controller.getFlags();
+ // We only care about sessions that handle transport controls,
+ // which will be true for apps using RCC
+ if ((flags & MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) {
+ updateController(controller);
+ return;
+ }
+ }
+ updateController(null);
+ }
+
+ }
+
//==================================================
// Event handling
private final EventHandler mEventHandler;
@@ -658,6 +770,8 @@
private final static int MSG_NEW_METADATA = 3; // msg always has non-null obj parameter
private final static int MSG_CLIENT_CHANGE = 4;
private final static int MSG_DISPLAY_ENABLE = 5;
+ private final static int MSG_NEW_PLAYBACK_STATE = 6;
+ private final static int MSG_NEW_MEDIA_METADATA = 7;
private class EventHandler extends Handler {
@@ -686,12 +800,46 @@
case MSG_DISPLAY_ENABLE:
onDisplayEnable(msg.arg1 == 1);
break;
+ case MSG_NEW_PLAYBACK_STATE:
+ // same as new playback info but using new apis
+ onNewPlaybackState((PlaybackState) msg.obj);
+ break;
+ case MSG_NEW_MEDIA_METADATA:
+ onNewMediaMetadata((MediaMetadata) msg.obj);
+ break;
default:
Log.e(TAG, "unknown event " + msg.what);
}
}
}
+ /**
+ * @hide
+ */
+ void startListeningToSessions() {
+ final ComponentName listenerComponent = new ComponentName(mContext,
+ mOnClientUpdateListener.getClass());
+ mSessionManager.addActiveSessionsListener(mSessionListener, listenerComponent,
+ ActivityManager.getCurrentUser());
+ mSessionListener.onActiveSessionsChanged(mSessionManager
+ .getActiveSessions(listenerComponent));
+ if (DEBUG) {
+ Log.d(TAG, "Registered session listener with component " + listenerComponent
+ + " for user " + ActivityManager.getCurrentUser());
+ }
+ }
+
+ /**
+ * @hide
+ */
+ void stopListeningToSessions() {
+ mSessionManager.removeActiveSessionsListener(mSessionListener);
+ if (DEBUG) {
+ Log.d(TAG, "Unregistered session listener for user "
+ + ActivityManager.getCurrentUser());
+ }
+ }
+
/** If the msg is already queued, replace it with this one. */
private static final int SENDMSG_REPLACE = 0;
/** If the msg is already queued, ignore this one and leave the old. */
@@ -713,6 +861,7 @@
handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delayMs);
}
+ ///////////// These calls are used by the old APIs with RCC and RCD //////////////////////
private void onNewPendingIntent(int genId, PendingIntent pi) {
synchronized(mGenLock) {
if (mClientGenerationIdCurrent != genId) {
@@ -848,6 +997,86 @@
}
}
+ ///////////// These calls are used by the new APIs with Sessions //////////////////////
+ private void updateController(MediaController controller) {
+ if (DEBUG) {
+ Log.d(TAG, "Updating controller to " + controller + " previous controller is "
+ + mCurrentSession);
+ }
+ synchronized (mInfoLock) {
+ if (controller == null) {
+ if (mCurrentSession != null) {
+ mCurrentSession.removeCallback(mSessionCb);
+ mCurrentSession = null;
+ sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
+ 0 /* genId */, 1 /* clearing */, null /* obj */, 0 /* delay */);
+ }
+ } else if (mCurrentSession == null
+ || !controller.getSessionInfo().getId()
+ .equals(mCurrentSession.getSessionInfo().getId())) {
+ if (mCurrentSession != null) {
+ mCurrentSession.removeCallback(mSessionCb);
+ }
+ sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
+ 0 /* genId */, 0 /* clearing */, null /* obj */, 0 /* delay */);
+ mCurrentSession = controller;
+ mCurrentSession.addCallback(mSessionCb, mEventHandler);
+
+ PlaybackState state = controller.getPlaybackState();
+ sendMsg(mEventHandler, MSG_NEW_PLAYBACK_STATE, SENDMSG_REPLACE,
+ 0 /* genId */, 0, state /* obj */, 0 /* delay */);
+
+ MediaMetadata metadata = controller.getMetadata();
+ sendMsg(mEventHandler, MSG_NEW_MEDIA_METADATA, SENDMSG_REPLACE,
+ 0 /* arg1 */, 0 /* arg2 */, metadata /* obj */, 0 /* delay */);
+ }
+ // else same controller, no need to update
+ }
+ }
+
+ private void onNewPlaybackState(PlaybackState state) {
+ final OnClientUpdateListener l;
+ synchronized (mInfoLock) {
+ l = this.mOnClientUpdateListener;
+ }
+ if (l != null) {
+ int playstate = state == null ? RemoteControlClient.PLAYSTATE_NONE : PlaybackState
+ .getRccStateFromState(state.getState());
+ if (state == null || state.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
+ l.onClientPlaybackStateUpdate(playstate);
+ } else {
+ l.onClientPlaybackStateUpdate(playstate, state.getLastPositionUpdateTime(),
+ state.getPosition(), state.getPlaybackRate());
+ }
+ if (state != null) {
+ l.onClientTransportControlUpdate(PlaybackState.getRccControlFlagsFromActions(state
+ .getActions()));
+ }
+ }
+ }
+
+ private void onNewMediaMetadata(MediaMetadata metadata) {
+ if (metadata == null) {
+ // RemoteController only handles non-null metadata
+ return;
+ }
+ final OnClientUpdateListener l;
+ final MetadataEditor metadataEditor;
+ // prepare the received Bundle to be used inside a MetadataEditor
+ synchronized(mInfoLock) {
+ l = mOnClientUpdateListener;
+ boolean canRate = mCurrentSession != null
+ && mCurrentSession.getRatingType() != Rating.RATING_NONE;
+ long editableKeys = canRate ? MediaMetadataEditor.RATING_KEY_BY_USER : 0;
+ Bundle legacyMetadata = MediaSessionLegacyHelper.getOldMetadata(metadata);
+ mMetadataEditor = new MetadataEditor(legacyMetadata, editableKeys);
+ metadataEditor = mMetadataEditor;
+ }
+ if (l != null) {
+ l.onClientMetadataUpdate(metadataEditor);
+ }
+ }
+
//==================================================
private static class PlaybackInfo {
int mState;
diff --git a/media/java/android/media/WebVttRenderer.java b/media/java/android/media/WebVttRenderer.java
index 1c9730f..7977988 100644
--- a/media/java/android/media/WebVttRenderer.java
+++ b/media/java/android/media/WebVttRenderer.java
@@ -1103,6 +1103,9 @@
*/
class WebVttRenderingWidget extends ViewGroup implements SubtitleTrack.RenderingWidget {
private static final boolean DEBUG = false;
+
+ private static final CaptionStyle DEFAULT_CAPTION_STYLE = CaptionStyle.DEFAULT;
+
private static final int DEBUG_REGION_BACKGROUND = 0x800000FF;
private static final int DEBUG_CUE_BACKGROUND = 0x80FF0000;
@@ -1144,7 +1147,8 @@
this(context, attrs, defStyleAttr, 0);
}
- public WebVttRenderingWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public WebVttRenderingWidget(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// Cannot render text over video when layer type is hardware.
@@ -1259,6 +1263,7 @@
}
private void setCaptionStyle(CaptionStyle captionStyle, float fontSize) {
+ captionStyle = DEFAULT_CAPTION_STYLE.applyStyle(captionStyle);
mCaptionStyle = captionStyle;
mFontSize = fontSize;
diff --git a/media/java/android/media/session/IActiveSessionsListener.aidl b/media/java/android/media/session/IActiveSessionsListener.aidl
new file mode 100644
index 0000000..e5e24bc
--- /dev/null
+++ b/media/java/android/media/session/IActiveSessionsListener.aidl
@@ -0,0 +1,26 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.session;
+
+import android.media.session.MediaSessionToken;
+
+/**
+ * Listens for changes to the list of active sessions.
+ * @hide
+ */
+oneway interface IActiveSessionsListener {
+ void onActiveSessionsChanged(in List<MediaSessionToken> sessions);
+}
\ No newline at end of file
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 9ce0692..f0cd785 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -19,6 +19,7 @@
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.session.ISessionControllerCallback;
+import android.media.session.MediaSessionInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -35,6 +36,8 @@
void unregisterCallbackListener(in ISessionControllerCallback cb);
boolean isTransportControlEnabled();
void showRoutePicker();
+ MediaSessionInfo getSessionInfo();
+ long getFlags();
// These commands are for the TransportController
void play();
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 6d9888f..bd1fa85 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -16,6 +16,7 @@
package android.media.session;
import android.content.ComponentName;
+import android.media.session.IActiveSessionsListener;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.os.Bundle;
@@ -30,4 +31,7 @@
List<IBinder> getSessions(in ComponentName compName, int userId);
void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags);
+ void addSessionsListener(in IActiveSessionsListener listener, in ComponentName compName,
+ int userId);
+ void removeSessionsListener(in IActiveSessionsListener listener);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index caff1ad..5ca7daa 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -57,6 +57,7 @@
private final Object mLock = new Object();
private boolean mCbRegistered = false;
+ private MediaSessionInfo mInfo;
private TransportControls mTransportController;
@@ -174,6 +175,21 @@
}
/**
+ * Get the flags for this session.
+ *
+ * @return The current set of flags for the session.
+ * @hide
+ */
+ public long getFlags() {
+ try {
+ return mSessionBinder.getFlags();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getFlags.", e);
+ }
+ return 0;
+ }
+
+ /**
* Adds a callback to receive updates from the Session. Updates will be
* posted on the caller's thread.
*
@@ -246,6 +262,23 @@
}
}
+ /**
+ * Get the info for the session this controller is connected to.
+ *
+ * @return The session info for the connected session.
+ * @hide
+ */
+ public MediaSessionInfo getSessionInfo() {
+ if (mInfo == null) {
+ try {
+ mInfo = mSessionBinder.getSessionInfo();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in getSessionInfo.", e);
+ }
+ }
+ return mInfo;
+ }
+
/*
* @hide
*/
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 7972639..4ba1351 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -277,6 +277,7 @@
throw new IllegalArgumentException("volumeProvider may not be null!");
}
mVolumeProvider = volumeProvider;
+ volumeProvider.setSession(this);
try {
mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(),
@@ -522,6 +523,24 @@
}
}
+ /**
+ * Notify the system that the remove volume changed.
+ *
+ * @param provider The provider that is handling volume changes.
+ * @hide
+ */
+ void notifyRemoteVolumeChanged(RemoteVolumeProvider provider) {
+ if (provider == null || provider != mVolumeProvider) {
+ Log.w(TAG, "Received update from stale volume provider");
+ return;
+ }
+ try {
+ mBinder.setCurrentVolume(provider.onGetCurrentVolume());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in notifyVolumeChanged", e);
+ }
+ }
+
private void dispatchPlay() {
postToTransportCallbacks(TransportMessageHandler.MSG_PLAY);
}
@@ -963,27 +982,26 @@
@Override
public void onRouteStateChange(int state) throws RemoteException {
// TODO
-
}
- /*
- * (non-Javadoc)
- * @see android.media.session.ISessionCallback#onAdjustVolumeBy(int)
- */
@Override
public void onAdjustVolumeBy(int delta) throws RemoteException {
- // TODO(epastern): Auto-generated method stub
-
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ if (session.mVolumeProvider != null) {
+ session.mVolumeProvider.onAdjustVolumeBy(delta);
+ }
+ }
}
- /*
- * (non-Javadoc)
- * @see android.media.session.ISessionCallback#onSetVolumeTo(int)
- */
@Override
public void onSetVolumeTo(int value) throws RemoteException {
- // TODO(epastern): Auto-generated method stub
-
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ if (session.mVolumeProvider != null) {
+ session.mVolumeProvider.onSetVolumeTo(value);
+ }
+ }
}
}
diff --git a/media/java/android/media/session/MediaSessionInfo.aidl b/media/java/android/media/session/MediaSessionInfo.aidl
new file mode 100644
index 0000000..63dca9a
--- /dev/null
+++ b/media/java/android/media/session/MediaSessionInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media.session;
+
+parcelable MediaSessionInfo;
diff --git a/media/java/android/media/session/MediaSessionInfo.java b/media/java/android/media/session/MediaSessionInfo.java
index f701211..4dc1c09 100644
--- a/media/java/android/media/session/MediaSessionInfo.java
+++ b/media/java/android/media/session/MediaSessionInfo.java
@@ -26,18 +26,21 @@
public final class MediaSessionInfo implements Parcelable {
private final String mId;
private final String mPackageName;
+ private final int mPid;
/**
* @hide
*/
- public MediaSessionInfo(String id, String packageName) {
+ public MediaSessionInfo(String id, String packageName, int pid) {
mId = id;
mPackageName = packageName;
+ mPid = pid;
}
private MediaSessionInfo(Parcel in) {
mId = in.readString();
mPackageName = in.readString();
+ mPid = in.readInt();
}
/**
@@ -58,9 +61,13 @@
return mId;
}
+ public int getPid() {
+ return mPid;
+ }
+
@Override
public String toString() {
- return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + "}";
+ return "SessionInfo {id=" + mId + ", pkg=" + mPackageName + ", pid=" + mPid + "}";
}
@Override
@@ -72,6 +79,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mId);
dest.writeString(mPackageName);
+ dest.writeInt(mPid);
}
public static final Parcelable.Creator<MediaSessionInfo> CREATOR
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 099f601..801844f 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -20,6 +20,10 @@
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
+import android.media.MediaMetadata;
+import android.media.MediaMetadataEditor;
+import android.media.MediaMetadataRetriever;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
@@ -64,6 +68,88 @@
return sInstance;
}
+ public static Bundle getOldMetadata(MediaMetadata metadata) {
+ Bundle oldMetadata = new Bundle();
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ALBUM),
+ metadata.getString(MediaMetadata.METADATA_KEY_ALBUM));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_ART)) {
+ oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
+ metadata.getBitmap(MediaMetadata.METADATA_KEY_ART));
+ } else if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART)) {
+ // Fall back to album art if the track art wasn't available
+ oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
+ metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ARTIST)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST),
+ metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ARTIST),
+ metadata.getString(MediaMetadata.METADATA_KEY_ARTIST));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_AUTHOR)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_AUTHOR),
+ metadata.getString(MediaMetadata.METADATA_KEY_AUTHOR));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_COMPILATION)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_COMPILATION),
+ metadata.getString(MediaMetadata.METADATA_KEY_COMPILATION));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_COMPOSER)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_COMPOSER),
+ metadata.getString(MediaMetadata.METADATA_KEY_COMPOSER));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_DATE)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DATE),
+ metadata.getString(MediaMetadata.METADATA_KEY_DATE));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_DISC_NUMBER)) {
+ oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER),
+ metadata.getLong(MediaMetadata.METADATA_KEY_DISC_NUMBER));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+ oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DURATION),
+ metadata.getLong(MediaMetadata.METADATA_KEY_DURATION));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_GENRE)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_GENRE),
+ metadata.getString(MediaMetadata.METADATA_KEY_GENRE));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) {
+ oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS),
+ metadata.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_RATING)) {
+ oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.RATING_KEY_BY_OTHERS),
+ metadata.getRating(MediaMetadata.METADATA_KEY_RATING));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_USER_RATING)) {
+ oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER),
+ metadata.getRating(MediaMetadata.METADATA_KEY_USER_RATING));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_TITLE),
+ metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) {
+ oldMetadata.putLong(
+ String.valueOf(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER),
+ metadata.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_WRITER)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_WRITER),
+ metadata.getString(MediaMetadata.METADATA_KEY_WRITER));
+ }
+ if (metadata.containsKey(MediaMetadata.METADATA_KEY_YEAR)) {
+ oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_YEAR),
+ metadata.getString(MediaMetadata.METADATA_KEY_YEAR));
+ }
+ return oldMetadata;
+ }
+
public MediaSession getSession(PendingIntent pi) {
SessionHolder holder = mSessions.get(pi);
return holder == null ? null : holder.mSession;
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 9e8b0d3..8eceee8 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -96,10 +96,13 @@
}
/**
- * Get a list of controllers for all ongoing sessions. This requires the
- * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
- * the calling app. You may also retrieve this list if your app is an
- * enabled notification listener using the
+ * Get a list of controllers for all ongoing sessions. The controllers will
+ * be provided in priority order with the most important controller at index
+ * 0.
+ * <p>
+ * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL
+ * permission be held by the calling app. You may also retrieve this list if
+ * your app is an enabled notification listener using the
* {@link NotificationListenerService} APIs, in which case you must pass the
* {@link ComponentName} of your enabled listener.
*
@@ -142,6 +145,50 @@
}
/**
+ * Add a listener to be notified when the list of active sessions
+ * changes.This requires the
+ * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
+ * the calling app. You may also retrieve this list if your app is an
+ * enabled notification listener using the
+ * {@link NotificationListenerService} APIs, in which case you must pass the
+ * {@link ComponentName} of your enabled listener.
+ *
+ * @param sessionListener The listener to add.
+ * @param notificationListener The enabled notification listener component.
+ * May be null.
+ * @param userId The userId to listen for changes on.
+ * @hide
+ */
+ public void addActiveSessionsListener(SessionListener sessionListener,
+ ComponentName notificationListener, int userId) {
+ if (sessionListener == null) {
+ throw new IllegalArgumentException("listener may not be null");
+ }
+ try {
+ mService.addSessionsListener(sessionListener.mStub, notificationListener, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in addActiveSessionsListener.", e);
+ }
+ }
+
+ /**
+ * Stop receiving active sessions updates on the specified listener.
+ *
+ * @param listener The listener to remove.
+ * @hide
+ */
+ public void removeActiveSessionsListener(SessionListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener may not be null");
+ }
+ try {
+ mService.removeSessionsListener(listener.mStub);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in removeActiveSessionsListener.", e);
+ }
+ }
+
+ /**
* Send a media key event. The receiver will be selected automatically.
*
* @param keyEvent The KeyEvent to send.
@@ -184,4 +231,36 @@
Log.e(TAG, "Failed to send adjust volume.", e);
}
}
+
+ /**
+ * Listens for changes to the list of active sessions. This can be added
+ * using {@link #addActiveSessionsListener}.
+ *
+ * @hide
+ */
+ public static abstract class SessionListener {
+ /**
+ * Called when the list of active sessions has changed. This can be due
+ * to a session being added or removed or the order of sessions
+ * changing. The controllers will be provided in priority order with the
+ * most important controller at index 0.
+ *
+ * @param controllers The updated list of controllers for the user that
+ * changed.
+ */
+ public abstract void onActiveSessionsChanged(List<MediaController> controllers);
+
+ private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() {
+ @Override
+ public void onActiveSessionsChanged(List<MediaSessionToken> tokens)
+ throws RemoteException {
+ ArrayList<MediaController> controllers = new ArrayList<MediaController>();
+ int size = tokens.size();
+ for (int i = 0; i < size; i++) {
+ controllers.add(MediaController.fromToken(tokens.get(i)));
+ }
+ SessionListener.this.onActiveSessionsChanged(controllers);
+ }
+ };
+ }
}
diff --git a/media/java/android/media/session/MediaSessionToken.java b/media/java/android/media/session/MediaSessionToken.java
index 86f5662..e599189 100644
--- a/media/java/android/media/session/MediaSessionToken.java
+++ b/media/java/android/media/session/MediaSessionToken.java
@@ -31,7 +31,7 @@
/**
* @hide
*/
- MediaSessionToken(ISessionController binder) {
+ public MediaSessionToken(ISessionController binder) {
mBinder = binder;
}
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index e09ac3f..3b3f249 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -432,6 +432,8 @@
return STATE_REWINDING;
case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
return STATE_SKIPPING_TO_PREVIOUS;
+ case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+ return STATE_SKIPPING_TO_NEXT;
case RemoteControlClient.PLAYSTATE_STOPPED:
return STATE_STOPPED;
default:
@@ -440,6 +442,41 @@
}
/**
+ * Get the {@link RemoteControlClient} state for the given
+ * {@link PlaybackState} state.
+ *
+ * @param state The state used by {@link PlaybackState}.
+ * @return The equivalent state used by {@link RemoteControlClient}.
+ * @hide
+ */
+ public static int getRccStateFromState(int state) {
+ switch (state) {
+ case STATE_BUFFERING:
+ return RemoteControlClient.PLAYSTATE_BUFFERING;
+ case STATE_ERROR:
+ return RemoteControlClient.PLAYSTATE_ERROR;
+ case STATE_FAST_FORWARDING:
+ return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
+ case STATE_NONE:
+ return RemoteControlClient.PLAYSTATE_NONE;
+ case STATE_PAUSED:
+ return RemoteControlClient.PLAYSTATE_PAUSED;
+ case STATE_PLAYING:
+ return RemoteControlClient.PLAYSTATE_PLAYING;
+ case STATE_REWINDING:
+ return RemoteControlClient.PLAYSTATE_REWINDING;
+ case STATE_SKIPPING_TO_PREVIOUS:
+ return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
+ case STATE_SKIPPING_TO_NEXT:
+ return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
+ case STATE_STOPPED:
+ return RemoteControlClient.PLAYSTATE_STOPPED;
+ default:
+ return -1;
+ }
+ }
+
+ /**
* @hide
*/
public static long getActionsFromRccControlFlags(int rccFlags) {
@@ -454,6 +491,21 @@
return actions;
}
+ /**
+ * @hide
+ */
+ public static int getRccControlFlagsFromActions(long actions) {
+ int rccFlags = 0;
+ long action = 1;
+ while (action <= actions && action < Integer.MAX_VALUE) {
+ if ((action & actions) != 0) {
+ rccFlags |= getRccFlagForAction(action);
+ }
+ action = action << 1;
+ }
+ return rccFlags;
+ }
+
private static long getActionForRccFlag(int flag) {
switch (flag) {
case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
@@ -480,6 +532,35 @@
return 0;
}
+ private static int getRccFlagForAction(long action) {
+ // We only care about the lower set of actions that can map to rcc
+ // flags.
+ int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
+ switch (testAction) {
+ case (int) ACTION_SKIP_TO_PREVIOUS:
+ return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
+ case (int) ACTION_REWIND:
+ return RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
+ case (int) ACTION_PLAY:
+ return RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
+ case (int) ACTION_PLAY_PAUSE:
+ return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
+ case (int) ACTION_PAUSE:
+ return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
+ case (int) ACTION_STOP:
+ return RemoteControlClient.FLAG_KEY_MEDIA_STOP;
+ case (int) ACTION_FAST_FORWARD:
+ return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
+ case (int) ACTION_SKIP_TO_NEXT:
+ return RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
+ case (int) ACTION_SEEK_TO:
+ return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
+ case (int) ACTION_SET_RATING:
+ return RemoteControlClient.FLAG_KEY_MEDIA_RATING;
+ }
+ return 0;
+ }
+
public static final Parcelable.Creator<PlaybackState> CREATOR
= new Parcelable.Creator<PlaybackState>() {
@Override
diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/session/RemoteVolumeProvider.java
index 47f672f3..606b1d7 100644
--- a/media/java/android/media/session/RemoteVolumeProvider.java
+++ b/media/java/android/media/session/RemoteVolumeProvider.java
@@ -15,6 +15,9 @@
*/
package android.media.session;
+import android.os.RemoteException;
+import android.util.Log;
+
/**
* Handles requests to adjust or set the volume on a session. This is also used
* to push volume updates back to the session after a request has been handled.
@@ -22,6 +25,7 @@
* {@link MediaSession#setPlaybackToRemote}.
*/
public abstract class RemoteVolumeProvider {
+ private static final String TAG = "RemoteVolumeProvider";
/**
* The volume is fixed and can not be modified. Requests to change volume
@@ -46,6 +50,8 @@
private final int mControlType;
private final int mMaxVolume;
+ private MediaSession mSession;
+
/**
* Create a new volume provider for handling volume events. You must specify
* the type of volume control and the maximum volume that can be used.
@@ -88,7 +94,7 @@
* Notify the system that the remote playback's volume has been changed.
*/
public final void notifyVolumeChanged() {
- // TODO
+ mSession.notifyRemoteVolumeChanged(this);
}
/**
@@ -107,4 +113,11 @@
*/
public void onAdjustVolumeBy(int delta) {
}
+
+ /**
+ * @hide
+ */
+ void setSession(MediaSession session) {
+ mSession = session;
+ }
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
new file mode 100644
index 0000000..011da35
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.content.ComponentName;
+import android.media.tv.ITvInputSession;
+import android.os.Bundle;
+import android.view.InputChannel;
+
+/**
+ * Interface a client of the ITvInputManager implements, to identify itself and receive information
+ * about changes to the state of each TV input service.
+ * @hide
+ */
+oneway interface ITvInputClient {
+ void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
+ void onAvailabilityChanged(in String inputId, boolean isAvailable);
+ void onSessionReleased(int seq);
+ void onSessionEvent(in String name, in Bundle args, int seq);
+ void onVideoStreamChanged(int width, int height, boolean interlaced, int seq);
+ void onAudioStreamChanged(int channelCount, int seq);
+ void onClosedCaptionStreamChanged(boolean hasClosedCaption, int seq);
+}
diff --git a/media/java/android/media/tv/ITvInputHardware.aidl b/media/java/android/media/tv/ITvInputHardware.aidl
new file mode 100644
index 0000000..f35e8f3
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputHardware.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.media.tv.TvStreamConfig;
+import android.view.KeyEvent;
+import android.view.Surface;
+
+/**
+ * TvInputService representing a physical port should connect to HAL through this interface.
+ * Framework will take care of communication among system services including TvInputManagerService,
+ * HdmiControlService, AudioService, etc.
+ *
+ * @hide
+ */
+interface ITvInputHardware {
+ /**
+ * Make the input render on the surface according to the config. In case of HDMI, this will
+ * trigger CEC commands for adjusting active HDMI source. Returns true on success.
+ */
+ boolean setSurface(in Surface surface, in TvStreamConfig config);
+ /**
+ * Set volume for this stream via AudioGain. (TBD)
+ */
+ void setVolume(float volume);
+
+ /**
+ * Dispatch key event to HDMI service. The events would be automatically converted to
+ * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail.
+ */
+ boolean dispatchKeyEventToHdmi(in KeyEvent event);
+}
diff --git a/media/java/android/media/tv/ITvInputHardwareCallback.aidl b/media/java/android/media/tv/ITvInputHardwareCallback.aidl
new file mode 100644
index 0000000..870883b
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputHardwareCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.media.tv.TvStreamConfig;
+
+/**
+ * @hide
+ */
+oneway interface ITvInputHardwareCallback {
+ void onReleased();
+ void onStreamConfigChanged(in TvStreamConfig[] configs);
+}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
new file mode 100644
index 0000000..6db5a18
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.media.tv.ITvInputHardware;
+import android.media.tv.ITvInputHardwareCallback;
+import android.media.tv.ITvInputClient;
+import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvInputInfo;
+import android.net.Uri;
+import android.view.Surface;
+
+/**
+ * Interface to the TV input manager service.
+ * @hide
+ */
+interface ITvInputManager {
+ List<TvInputInfo> getTvInputList(int userId);
+
+ boolean getAvailability(in ITvInputClient client, in String inputId, int userId);
+
+ void registerCallback(in ITvInputClient client, in String inputId, int userId);
+ void unregisterCallback(in ITvInputClient client, in String inputId, int userId);
+
+ void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
+ void releaseSession(in IBinder sessionToken, int userId);
+
+ void setSurface(in IBinder sessionToken, in Surface surface, int userId);
+ void setVolume(in IBinder sessionToken, float volume, int userId);
+ void tune(in IBinder sessionToken, in Uri channelUri, int userId);
+
+ void createOverlayView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
+ int userId);
+ void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
+ void removeOverlayView(in IBinder sessionToken, int userId);
+
+ // For TV input hardware binding
+ List<TvInputHardwareInfo> getHardwareList();
+ ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
+ int userId);
+ void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
+}
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
new file mode 100644
index 0000000..992e424
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.media.tv.ITvInputServiceCallback;
+import android.media.tv.ITvInputSessionCallback;
+import android.view.InputChannel;
+
+/**
+ * Top-level interface to a TV input component (implemented in a Service).
+ * @hide
+ */
+oneway interface ITvInputService {
+ void registerCallback(ITvInputServiceCallback callback);
+ void unregisterCallback(in ITvInputServiceCallback callback);
+ void createSession(in InputChannel channel, ITvInputSessionCallback callback);
+}
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
new file mode 100644
index 0000000..c9484dd
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.content.ComponentName;
+
+/**
+ * Helper interface for ITvInputService to allow the TV input to notify the client when its status
+ * has been changed.
+ * @hide
+ */
+oneway interface ITvInputServiceCallback {
+ void onAvailabilityChanged(in String inputId, boolean isAvailable);
+}
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
new file mode 100644
index 0000000..fb2e251
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -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 android.media.tv;
+
+import android.graphics.Rect;
+import android.net.Uri;
+import android.view.Surface;
+
+/**
+ * Sub-interface of ITvInputService which is created per session and has its own context.
+ * @hide
+ */
+oneway interface ITvInputSession {
+ void release();
+
+ void setSurface(in Surface surface);
+ // TODO: Remove this once it becomes irrelevant for applications to handle audio focus. The plan
+ // is to introduce some new concepts that will solve a number of problems in audio policy today.
+ void setVolume(float volume);
+ void tune(in Uri channelUri);
+
+ void createOverlayView(in IBinder windowToken, in Rect frame);
+ void relayoutOverlayView(in Rect frame);
+ void removeOverlayView();
+}
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
new file mode 100644
index 0000000..00f2922a
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.media.tv.ITvInputSession;
+import android.os.Bundle;
+
+/**
+ * Helper interface for ITvInputSession to allow the TV input to notify the system service when a
+ * new session has been created.
+ * @hide
+ */
+oneway interface ITvInputSessionCallback {
+ void onSessionCreated(ITvInputSession session);
+ void onSessionEvent(in String name, in Bundle args);
+ void onVideoStreamChanged(int width, int height, boolean interlaced);
+ void onAudioStreamChanged(int channelCount);
+ void onClosedCaptionStreamChanged(boolean hasClosedCaption);
+}
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
new file mode 100644
index 0000000..d20ee0e
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.Surface;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+/**
+ * Implements the internal ITvInputSession interface to convert incoming calls on to it back to
+ * calls on the public TvInputSession interface, scheduling them on the main thread of the process.
+ *
+ * @hide
+ */
+public class ITvInputSessionWrapper extends ITvInputSession.Stub implements HandlerCaller.Callback {
+ private static final String TAG = "TvInputSessionWrapper";
+
+ private static final int DO_RELEASE = 1;
+ private static final int DO_SET_SURFACE = 2;
+ private static final int DO_SET_VOLUME = 3;
+ private static final int DO_TUNE = 4;
+ private static final int DO_CREATE_OVERLAY_VIEW = 5;
+ private static final int DO_RELAYOUT_OVERLAY_VIEW = 6;
+ private static final int DO_REMOVE_OVERLAY_VIEW = 7;
+
+ private final HandlerCaller mCaller;
+
+ private TvInputService.Session mTvInputSessionImpl;
+ private InputChannel mChannel;
+ private TvInputEventReceiver mReceiver;
+
+ public ITvInputSessionWrapper(Context context, TvInputService.Session sessionImpl,
+ InputChannel channel) {
+ mCaller = new HandlerCaller(context, null, this, true /* asyncHandler */);
+ mTvInputSessionImpl = sessionImpl;
+ mChannel = channel;
+ if (channel != null) {
+ mReceiver = new TvInputEventReceiver(channel, context.getMainLooper());
+ }
+ }
+
+ @Override
+ public void executeMessage(Message msg) {
+ if (mTvInputSessionImpl == null) {
+ return;
+ }
+
+ switch (msg.what) {
+ case DO_RELEASE: {
+ mTvInputSessionImpl.release();
+ mTvInputSessionImpl = null;
+ if (mReceiver != null) {
+ mReceiver.dispose();
+ mReceiver = null;
+ }
+ if (mChannel != null) {
+ mChannel.dispose();
+ mChannel = null;
+ }
+ return;
+ }
+ case DO_SET_SURFACE: {
+ mTvInputSessionImpl.setSurface((Surface) msg.obj);
+ return;
+ }
+ case DO_SET_VOLUME: {
+ mTvInputSessionImpl.setVolume((Float) msg.obj);
+ return;
+ }
+ case DO_TUNE: {
+ mTvInputSessionImpl.tune((Uri) msg.obj);
+ return;
+ }
+ case DO_CREATE_OVERLAY_VIEW: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ mTvInputSessionImpl.createOverlayView((IBinder) args.arg1, (Rect) args.arg2);
+ args.recycle();
+ return;
+ }
+ case DO_RELAYOUT_OVERLAY_VIEW: {
+ mTvInputSessionImpl.relayoutOverlayView((Rect) msg.obj);
+ return;
+ }
+ case DO_REMOVE_OVERLAY_VIEW: {
+ mTvInputSessionImpl.removeOverlayView(true);
+ return;
+ }
+ default: {
+ Log.w(TAG, "Unhandled message code: " + msg.what);
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void release() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_RELEASE));
+ }
+
+ @Override
+ public void setSurface(Surface surface) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_SURFACE, surface));
+ }
+
+ @Override
+ public final void setVolume(float volume) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_VOLUME, volume));
+ }
+
+ @Override
+ public void tune(Uri channelUri) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_TUNE, channelUri));
+ }
+
+ @Override
+ public void createOverlayView(IBinder windowToken, Rect frame) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_OVERLAY_VIEW, windowToken,
+ frame));
+ }
+
+ @Override
+ public void relayoutOverlayView(Rect frame) {
+ mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_RELAYOUT_OVERLAY_VIEW, frame));
+ }
+
+ @Override
+ public void removeOverlayView() {
+ mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_OVERLAY_VIEW));
+ }
+
+ private final class TvInputEventReceiver extends InputEventReceiver {
+ public TvInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (mTvInputSessionImpl == null) {
+ // The session has been finished.
+ finishInputEvent(event, false);
+ return;
+ }
+
+ int handled = mTvInputSessionImpl.dispatchInputEvent(event, this);
+ if (handled != TvInputManager.Session.DISPATCH_IN_PROGRESS) {
+ finishInputEvent(event, handled == TvInputManager.Session.DISPATCH_HANDLED);
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
new file mode 100644
index 0000000..7ff2c2e
--- /dev/null
+++ b/media/java/android/media/tv/TvContract.java
@@ -0,0 +1,897 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+import java.util.List;
+
+/**
+ * <p>
+ * The contract between the TV provider and applications. Contains definitions for the supported
+ * URIs and columns.
+ * </p>
+ * <h3>Overview</h3>
+ * <p>
+ * TvContract defines a basic database of TV content metadata such as channel and program
+ * information. The information is stored in {@link Channels} and {@link Programs} tables.
+ * </p>
+ * <ul>
+ * <li>A row in the {@link Channels} table represents information about a TV channel. The data
+ * format can vary greatly from standard to standard or according to service provider, thus
+ * the columns here are mostly comprised of basic entities that are usually seen to users
+ * regardless of standard such as channel number and name.</li>
+ * <li>A row in the {@link Programs} table represents a set of data describing a TV program such
+ * as program title and start time.</li>
+ * </ul>
+ */
+public final class TvContract {
+ /** The authority for the TV provider. */
+ public static final String AUTHORITY = "android.media.tv";
+
+ private static final String PATH_CHANNEL = "channel";
+ private static final String PATH_PROGRAM = "program";
+ private static final String PATH_INPUT = "input";
+
+ /**
+ * An optional query, update or delete URI parameter that allows the caller to specify start
+ * time (in milliseconds since the epoch) to filter programs.
+ *
+ * @hide
+ */
+ public static final String PARAM_START_TIME = "start_time";
+
+ /**
+ * An optional query, update or delete URI parameter that allows the caller to specify end time
+ * (in milliseconds since the epoch) to filter programs.
+ *
+ * @hide
+ */
+ public static final String PARAM_END_TIME = "end_time";
+
+ /**
+ * A query, update or delete URI parameter that allows the caller to operate on all or
+ * browsable-only channels. If set to "true", the rows that contain non-browsable channels are
+ * not affected.
+ *
+ * @hide
+ */
+ public static final String PARAM_BROWSABLE_ONLY = "browsable_only";
+
+ /**
+ * Builds a URI that points to a specific channel.
+ *
+ * @param channelId The ID of the channel to point to.
+ */
+ public static final Uri buildChannelUri(long channelId) {
+ return ContentUris.withAppendedId(Channels.CONTENT_URI, channelId);
+ }
+
+ /**
+ * Builds a URI that points to a channel logo. See {@link Channels.Logo}.
+ *
+ * @param channelId The ID of the channel whose logo is pointed to.
+ */
+ public static final Uri buildChannelLogoUri(long channelId) {
+ return buildChannelLogoUri(buildChannelUri(channelId));
+ }
+
+ /**
+ * Builds a URI that points to a channel logo. See {@link Channels.Logo}.
+ *
+ * @param channelUri The URI of the channel whose logo is pointed to.
+ */
+ public static final Uri buildChannelLogoUri(Uri channelUri) {
+ if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
+ throw new IllegalArgumentException("Not a channel: " + channelUri);
+ }
+ return Uri.withAppendedPath(channelUri, Channels.Logo.CONTENT_DIRECTORY);
+ }
+
+ /**
+ * Builds a URI that points to all browsable channels from a given TV input.
+ *
+ * @param name {@link ComponentName} of the {@link android.media.tv.TvInputService} that
+ * implements the given TV input.
+ */
+ public static final Uri buildChannelsUriForInput(ComponentName name) {
+ return buildChannelsUriForInput(name, true);
+ }
+
+ /**
+ * Builds a URI that points to all or browsable-only channels from a given TV input.
+ *
+ * @param name {@link ComponentName} of the {@link android.media.tv.TvInputService} that
+ * implements the given TV input.
+ * @param browsableOnly If set to {@code true} the URI points to only browsable channels. If set
+ * to {@code false} the URI points to all channels regardless of whether they are
+ * browsable or not.
+ */
+ public static final Uri buildChannelsUriForInput(ComponentName name, boolean browsableOnly) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_INPUT).appendPath(name.getPackageName())
+ .appendPath(name.getClassName()).appendPath(PATH_CHANNEL)
+ .appendQueryParameter(PARAM_BROWSABLE_ONLY, String.valueOf(browsableOnly)).build();
+ }
+
+ /**
+ * Builds a URI that points to a specific program.
+ *
+ * @param programId The ID of the program to point to.
+ */
+ public static final Uri buildProgramUri(long programId) {
+ return ContentUris.withAppendedId(Programs.CONTENT_URI, programId);
+ }
+
+ /**
+ * Builds a URI that points to all programs on a given channel.
+ *
+ * @param channelUri The URI of the channel to return programs for.
+ */
+ public static final Uri buildProgramsUriForChannel(Uri channelUri) {
+ if (!PATH_CHANNEL.equals(channelUri.getPathSegments().get(0))) {
+ throw new IllegalArgumentException("Not a channel: " + channelUri);
+ }
+ String channelId = String.valueOf(ContentUris.parseId(channelUri));
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY)
+ .appendPath(PATH_CHANNEL).appendPath(channelId).appendPath(PATH_PROGRAM).build();
+ }
+
+ /**
+ * Builds a URI that points to programs on a specific channel whose schedules overlap with the
+ * given time frame.
+ *
+ * @param channelUri The URI of the channel to return programs for.
+ * @param startTime The start time used to filter programs. The returned programs should have
+ * {@link Programs#COLUMN_END_TIME_UTC_MILLIS} that is greater than this time.
+ * @param endTime The end time used to filter programs. The returned programs should have
+ * {@link Programs#COLUMN_START_TIME_UTC_MILLIS} that is less than this time.
+ */
+ public static final Uri buildProgramsUriForChannel(Uri channelUri, long startTime,
+ long endTime) {
+ Uri uri = buildProgramsUriForChannel(channelUri);
+ return uri.buildUpon().appendQueryParameter(PARAM_START_TIME, String.valueOf(startTime))
+ .appendQueryParameter(PARAM_END_TIME, String.valueOf(endTime)).build();
+ }
+
+ /**
+ * Builds a URI that points to a specific program the user watched.
+ *
+ * @param watchedProgramId The ID of the watched program to point to.
+ * @hide
+ */
+ public static final Uri buildWatchedProgramUri(long watchedProgramId) {
+ return ContentUris.withAppendedId(WatchedPrograms.CONTENT_URI, watchedProgramId);
+ }
+
+ /**
+ * Extracts the {@link Channels#COLUMN_PACKAGE_NAME} from a given URI.
+ *
+ * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
+ * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+ * @hide
+ */
+ public static final String getPackageName(Uri channelsUri) {
+ final List<String> paths = channelsUri.getPathSegments();
+ if (paths.size() < 4) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ return paths.get(1);
+ }
+
+ /**
+ * Extracts the {@link Channels#COLUMN_SERVICE_NAME} from a given URI.
+ *
+ * @param channelsUri A URI constructed by {@link #buildChannelsUriForInput(ComponentName)} or
+ * {@link #buildChannelsUriForInput(ComponentName, boolean)}.
+ * @hide
+ */
+ public static final String getServiceName(Uri channelsUri) {
+ final List<String> paths = channelsUri.getPathSegments();
+ if (paths.size() < 4) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ if (!PATH_INPUT.equals(paths.get(0)) || !PATH_CHANNEL.equals(paths.get(3))) {
+ throw new IllegalArgumentException("Not channels: " + channelsUri);
+ }
+ return paths.get(2);
+ }
+
+ /**
+ * Extracts the {@link Channels#_ID} from a given URI.
+ *
+ * @param programsUri A URI constructed by {@link #buildProgramsUriForChannel(Uri)} or
+ * {@link #buildProgramsUriForChannel(Uri, long, long)}.
+ * @hide
+ */
+ public static final String getChannelId(Uri programsUri) {
+ final List<String> paths = programsUri.getPathSegments();
+ if (paths.size() < 3) {
+ throw new IllegalArgumentException("Not programs: " + programsUri);
+ }
+ if (!PATH_CHANNEL.equals(paths.get(0)) || !PATH_PROGRAM.equals(paths.get(2))) {
+ throw new IllegalArgumentException("Not programs: " + programsUri);
+ }
+ return paths.get(1);
+ }
+
+
+ private TvContract() {}
+
+ /**
+ * Common base for the tables of TV channels/programs.
+ */
+ public interface BaseTvColumns extends BaseColumns {
+ /**
+ * The name of the package that owns a row in each table.
+ * <p>
+ * The TV provider fills it in with the name of the package that provides the initial data
+ * of that row. If the package is later uninstalled, the rows it owns are automatically
+ * removed from the tables.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_PACKAGE_NAME = "package_name";
+ }
+
+ /** Column definitions for the TV channels table. */
+ public static final class Channels implements BaseTvColumns {
+
+ /** The content:// style URI for this table. */
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_CHANNEL);
+
+ /** The MIME type of a directory of TV channels. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
+
+ /** The MIME type of a single TV channel. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
+
+ /** A generic channel type. */
+ public static final int TYPE_OTHER = 0x0;
+
+ /** The channel type for NTSC. */
+ public static final int TYPE_NTSC = 0x1;
+
+ /** The channel type for PAL. */
+ public static final int TYPE_PAL = 0x2;
+
+ /** The channel type for SECAM. */
+ public static final int TYPE_SECAM = 0x3;
+
+ /** The special channel type used for pass-through inputs such as HDMI. */
+ public static final int TYPE_PASSTHROUGH = 0x00010000;
+
+ /** The channel type for DVB-T (terrestrial). */
+ public static final int TYPE_DVB_T = 0x00020000;
+
+ /** The channel type for DVB-T2 (terrestrial). */
+ public static final int TYPE_DVB_T2 = 0x00020001;
+
+ /** The channel type for DVB-S (satellite). */
+ public static final int TYPE_DVB_S = 0x00020100;
+
+ /** The channel type for DVB-S2 (satellite). */
+ public static final int TYPE_DVB_S2 = 0x00020101;
+
+ /** The channel type for DVB-C (cable). */
+ public static final int TYPE_DVB_C = 0x00020200;
+
+ /** The channel type for DVB-C2 (cable). */
+ public static final int TYPE_DVB_C2 = 0x00020201;
+
+ /** The channel type for DVB-H (handheld). */
+ public static final int TYPE_DVB_H = 0x00020300;
+
+ /** The channel type for DVB-SH (satellite). */
+ public static final int TYPE_DVB_SH = 0x00020400;
+
+ /** The channel type for ATSC (terrestrial). */
+ public static final int TYPE_ATSC_T = 0x00030000;
+
+ /** The channel type for ATSC (cable). */
+ public static final int TYPE_ATSC_C = 0x00030200;
+
+ /** The channel type for ATSC-M/H (mobile/handheld). */
+ public static final int TYPE_ATSC_M_H = 0x00030300;
+
+ /** The channel type for ISDB-T (terrestrial). */
+ public static final int TYPE_ISDB_T = 0x00040000;
+
+ /** The channel type for ISDB-Tb (Brazil). */
+ public static final int TYPE_ISDB_TB = 0x00040100;
+
+ /** The channel type for ISDB-S (satellite). */
+ public static final int TYPE_ISDB_S = 0x00040200;
+
+ /** The channel type for ISDB-C (cable). */
+ public static final int TYPE_ISDB_C = 0x00040300;
+
+ /** The channel type for 1seg (handheld). */
+ public static final int TYPE_1SEG = 0x00040400;
+
+ /** The channel type for DTMB (terrestrial). */
+ public static final int TYPE_DTMB = 0x00050000;
+
+ /** The channel type for CMMB (handheld). */
+ public static final int TYPE_CMMB = 0x00050100;
+
+ /** The channel type for T-DMB (terrestrial). */
+ public static final int TYPE_T_DMB = 0x00060000;
+
+ /** The channel type for S-DMB (satellite). */
+ public static final int TYPE_S_DMB = 0x00060100;
+
+ /** A generic service type. */
+ public static final int SERVICE_TYPE_OTHER = 0x0;
+
+ /** The service type for regular TV channels that have both audio and video. */
+ public static final int SERVICE_TYPE_AUDIO_VIDEO = 0x1;
+
+ /** The service type for radio channels that have audio only. */
+ public static final int SERVICE_TYPE_AUDIO = 0x2;
+
+ /**
+ * The name of the {@link TvInputService} subclass that provides this TV channel. This
+ * should be a fully qualified class name (such as, "com.example.project.TvInputService").
+ * <p>
+ * This is a required field.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_SERVICE_NAME = "service_name";
+
+ /**
+ * The predefined type of this TV channel.
+ * <p>
+ * This is primarily used to indicate which broadcast standard (e.g. ATSC, DVB or ISDB) the
+ * current channel conforms to, with an exception being {@link #TYPE_PASSTHROUGH}, which is
+ * a special channel type used only by pass-through inputs such as HDMI. The value should
+ * match to one of the followings: {@link #TYPE_OTHER}, {@link #TYPE_PASSTHROUGH},
+ * {@link #TYPE_DVB_T}, {@link #TYPE_DVB_T2}, {@link #TYPE_DVB_S}, {@link #TYPE_DVB_S2},
+ * {@link #TYPE_DVB_C}, {@link #TYPE_DVB_C2}, {@link #TYPE_DVB_H}, {@link #TYPE_DVB_SH},
+ * {@link #TYPE_ATSC_T}, {@link #TYPE_ATSC_C}, {@link #TYPE_ATSC_M_H}, {@link #TYPE_ISDB_T},
+ * {@link #TYPE_ISDB_TB}, {@link #TYPE_ISDB_S}, {@link #TYPE_ISDB_C} {@link #TYPE_1SEG},
+ * {@link #TYPE_DTMB}, {@link #TYPE_CMMB}, {@link #TYPE_T_DMB}, {@link #TYPE_S_DMB}
+ * </p><p>
+ * This is a required field.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_TYPE = "type";
+
+ /**
+ * The predefined service type of this TV channel.
+ * <p>
+ * This is primarily used to indicate whether the current channel is a regular TV channel or
+ * a radio-like channel. Use the same coding for {@code service_type} in the underlying
+ * broadcast standard if it is defined there (e.g. ATSC A/53, ETSI EN 300 468 and ARIB
+ * STD-B10). Otherwise use one of the followings: {@link #SERVICE_TYPE_OTHER},
+ * {@link #SERVICE_TYPE_AUDIO_VIDEO}, {@link #SERVICE_TYPE_AUDIO}
+ * </p><p>
+ * This is a required field.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_SERVICE_TYPE = "service_type";
+
+ /**
+ * The original network ID of this TV channel.
+ * <p>
+ * This is used to identify the originating delivery system, if applicable. Use the same
+ * coding for {@code original_network_id} in the underlying broadcast standard if it is
+ * defined there (e.g. ETSI EN 300 468/TR 101 211 and ARIB STD-B10). If channels cannot be
+ * globally identified by 2-tuple {{@link #COLUMN_TRANSPORT_STREAM_ID},
+ * {@link #COLUMN_SERVICE_ID}}, one must carefully assign a value to this field to form a
+ * unique 3-tuple identification {{@link #COLUMN_ORIGINAL_NETWORK_ID},
+ * {@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}} for its channels.
+ * </p><p>
+ * This is a required field if the channel cannot be uniquely identified by a 2-tuple
+ * {{@link #COLUMN_TRANSPORT_STREAM_ID}, {@link #COLUMN_SERVICE_ID}}.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+
+ /**
+ * The transport stream ID of this channel.
+ * <p>
+ * This is used to identify the Transport Stream that contains the current channel from any
+ * other multiplex within a network, if applicable. Use the same coding for
+ * {@code transport_stream_id} defined in ISO/IEC 13818-1 if the channel is transmitted via
+ * the MPEG Transport Stream as is the case for many digital broadcast standards.
+ * </p><p>
+ * This is a required field if the current channel is transmitted via the MPEG Transport
+ * Stream.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
+
+ /**
+ * The service ID of this channel.
+ * <p>
+ * This is used to identify the current service (roughly equivalent to channel) from any
+ * other service within the Transport Stream, if applicable. Use the same coding for
+ * {@code service_id} in the underlying broadcast standard if it is defined there (e.g. ETSI
+ * EN 300 468 and ARIB STD-B10) or {@code program_number} (which usually has the same value
+ * as {@code service_id}) in ISO/IEC 13818-1 if the channel is transmitted via the MPEG
+ * Transport Stream.
+ * </p><p>
+ * This is a required field if the current channel is transmitted via the MPEG Transport
+ * Stream.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_SERVICE_ID = "service_id";
+
+ /**
+ * The channel number that is displayed to the user.
+ * <p>
+ * The format can vary depending on broadcast standard and product specification.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_DISPLAY_NUMBER = "display_number";
+
+ /**
+ * The channel name that is displayed to the user.
+ * <p>
+ * A call sign is a good candidate to use for this purpose but any name that helps the user
+ * recognize the current channel will be enough. Can also be empty depending on broadcast
+ * standard.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_DISPLAY_NAME = "display_name";
+
+ /**
+ * The description of this TV channel.
+ * <p>
+ * Can be empty initially.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_DESCRIPTION = "description";
+
+ /**
+ * The flag indicating whether this TV channel is browsable or not.
+ * <p>
+ * A value of 1 indicates the channel is included in the channel list that applications use
+ * to browse channels, a value of 0 indicates the channel is not included in the list. If
+ * not specified, this value is set to 1 (browsable) by default.
+ * </p><p>
+ * Type: INTEGER (boolean)
+ * </p>
+ */
+ public static final String COLUMN_BROWSABLE = "browsable";
+
+ /**
+ * The flag indicating whether this TV channel is searchable or not.
+ * <p>
+ * In some regions, it is not allowed to surface search results for a given channel without
+ * broadcaster's consent. This is used to impose such restriction. A value of 1 indicates
+ * the channel is searchable and can be included in search results, a value of 0 indicates
+ * the channel and its TV programs are hidden from search. If not specified, this value is
+ * set to 1 (searchable) by default.
+ * </p>
+ * <p>
+ * Type: INTEGER (boolean)
+ * </p>
+ */
+ public static final String COLUMN_SEARCHABLE = "searchable";
+
+ /**
+ * The flag indicating whether this TV channel is locked or not.
+ * <p>
+ * This is primarily used for alternative parental control to prevent unauthorized users
+ * from watching the current channel regardless of the content rating. A value of 1
+ * indicates the channel is locked and the user is required to enter passcode to unlock it
+ * in order to watch the current program from the channel, a value of 0 indicates the
+ * channel is not locked thus the user is not prompted to enter passcode If not specified,
+ * this value is set to 0 (not locked) by default.
+ * </p><p>
+ * Type: INTEGER (boolean)
+ * </p>
+ * @hide
+ */
+ public static final String COLUMN_LOCKED = "locked";
+
+ /**
+ * Internal data used by individual TV input services.
+ * <p>
+ * This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ * </p><p>
+ * Type: BLOB
+ * </p>
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+
+ /**
+ * The version number of this row entry used by TV input services.
+ * <p>
+ * This is best used by sync adapters to identify the rows to update. The number can be
+ * defined by individual TV input services. One may assign the same value as
+ * {@code version_number} that appears in ETSI EN 300 468 or ATSC A/65, if the data are
+ * coming from a TV broadcast.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_VERSION_NUMBER = "version_number";
+
+ private Channels() {}
+
+ /**
+ * A sub-directory of a single TV channel that represents its primary logo.
+ * <p>
+ * To access this directory, append {@link Channels.Logo#CONTENT_DIRECTORY} to the raw
+ * channel URI. The resulting URI represents an image file, and should be interacted
+ * using ContentResolver.openAssetFileDescriptor.
+ * </p>
+ * <p>
+ * Note that this sub-directory also supports opening the logo as an asset file in write
+ * mode. Callers can create or replace the primary logo associated with this channel by
+ * opening the asset file and writing the full-size photo contents into it. When the file
+ * is closed, the image will be parsed, sized down if necessary, and stored.
+ * </p>
+ * <p>
+ * Usage example:
+ * <pre>
+ * public void writeChannelLogo(long channelId, byte[] logo) {
+ * Uri channelLogoUri = TvContract.buildChannelLogoUri(channelId);
+ * try {
+ * AssetFileDescriptor fd =
+ * getContentResolver().openAssetFileDescriptor(channelLogoUri, "rw");
+ * OutputStream os = fd.createOutputStream();
+ * os.write(logo);
+ * os.close();
+ * fd.close();
+ * } catch (IOException e) {
+ * // Handle error cases.
+ * }
+ * }
+ * </pre>
+ * </p>
+ */
+ public static final class Logo {
+
+ /**
+ * The directory twig for this sub-table.
+ */
+ public static final String CONTENT_DIRECTORY = "logo";
+
+ private Logo() {}
+ }
+ }
+
+ /** Column definitions for the TV programs table. */
+ public static final class Programs implements BaseTvColumns {
+
+ /** The content:// style URI for this table. */
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/"
+ + PATH_PROGRAM);
+
+ /** The MIME type of a directory of TV programs. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/program";
+
+ /** The MIME type of a single TV program. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
+
+ /**
+ * The ID of the TV channel that contains this TV program.
+ * <p>
+ * This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
+ * </p><p>
+ * Type: INTEGER (long)
+ * </p>
+ */
+ public static final String COLUMN_CHANNEL_ID = "channel_id";
+
+ /**
+ * The title of this TV program.
+ * <p>
+ * Type: TEXT
+ * </p>
+ **/
+ public static final String COLUMN_TITLE = "title";
+
+ /**
+ * The start time of this TV program, in milliseconds since the epoch.
+ * <p>
+ * Type: INTEGER (long)
+ * </p>
+ */
+ public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+
+ /**
+ * The end time of this TV program, in milliseconds since the epoch.
+ * <p>
+ * Type: INTEGER (long)
+ * </p>
+ */
+ public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+
+ /**
+ * The comma-separated genre string of this TV program.
+ * <p>
+ * Use the same language appeared in the underlying broadcast standard, if applicable. (For
+ * example, one can refer to the genre strings used in Genre Descriptor of ATSC A/65 or
+ * Content Descriptor of ETSI EN 300 468, if appropriate.) Otherwise, leave empty.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_BROADCAST_GENRE = "broadcast_genre";
+
+ /**
+ * The comma-separated canonical genre string of this TV program.
+ * <p>
+ * Canonical genres are defined in {@link Genres}. Use {@link Genres#encode Genres.encode()}
+ * to create a text that can be stored in this column. Use {@link Genres#decode
+ * Genres.decode()} to get the canonical genre strings from the text stored in this column.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ * @see Genres
+ */
+ public static final String COLUMN_CANONICAL_GENRE = "canonical_genre";
+
+ /**
+ * The short description of this TV program that is displayed to the user by default.
+ * <p>
+ * It is recommended to limit the length of the descriptions to 256 characters.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_SHORT_DESCRIPTION = "short_description";
+
+ /**
+ * The detailed, lengthy description of this TV program that is displayed only when the user
+ * wants to see more information.
+ * <p>
+ * TV input services should leave this field empty if they have no additional details beyond
+ * {@link #COLUMN_SHORT_DESCRIPTION}.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_LONG_DESCRIPTION = "long_description";
+
+ /**
+ * The comma-separated audio languages of this TV program.
+ * <p>
+ * This is used to describe available audio languages included in the program. Use
+ * 3-character language code as specified by ISO 639-2.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_AUDIO_LANGUAGE = "audio_language";
+
+ /**
+ * The URI for the poster art of this TV program.
+ * <p>
+ * Can be empty.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_POSTER_ART_URI = "poster_art_uri";
+
+ /**
+ * The URI for the thumbnail of this TV program.
+ * <p>
+ * Can be empty.
+ * </p><p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
+
+ /**
+ * Internal data used by individual TV input services.
+ * <p>
+ * This is internal to the provider that inserted it, and should not be decoded by other
+ * apps.
+ * </p><p>
+ * Type: BLOB
+ * </p>
+ */
+ public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+
+ /**
+ * The version number of this row entry used by TV input services.
+ * <p>
+ * This is best used by sync adapters to identify the rows to update. The number can be
+ * defined by individual TV input services. One may assign the same value as
+ * {@code version_number} in ETSI EN 300 468 or ATSC A/65, if the data are coming from a TV
+ * broadcast.
+ * </p><p>
+ * Type: INTEGER
+ * </p>
+ */
+ public static final String COLUMN_VERSION_NUMBER = "version_number";
+
+ private Programs() {}
+
+ /** Canonical genres for TV programs. */
+ public static final class Genres {
+ /** The genre for Family/Kids. */
+ public static final String FAMILY_KIDS = "Family/Kids";
+
+ /** The genre for Sports. */
+ public static final String SPORTS = "Sports";
+
+ /** The genre for Shopping. */
+ public static final String SHOPPING = "Shopping";
+
+ /** The genre for Movies. */
+ public static final String MOVIES = "Movies";
+
+ /** The genre for Comedy. */
+ public static final String COMEDY = "Comedy";
+
+ /** The genre for Travel. */
+ public static final String TRAVEL = "Travel";
+
+ /** The genre for Drama. */
+ public static final String DRAMA = "Drama";
+
+ /** The genre for Education. */
+ public static final String EDUCATION = "Education";
+
+ /** The genre for Animal/Wildlife. */
+ public static final String ANIMAL_WILDLIFE = "Animal/Wildlife";
+
+ /** The genre for News. */
+ public static final String NEWS = "News";
+
+ /** The genre for Gaming. */
+ public static final String GAMING = "Gaming";
+
+ private Genres() {}
+
+ /**
+ * Encodes canonical genre strings to a text that can be put into the database.
+ *
+ * @param genres Canonical genre strings. Use the strings defined in this class.
+ * @return an encoded genre string that can be inserted into the
+ * {@link #COLUMN_CANONICAL_GENRE} column.
+ */
+ public static String encode(String... genres) {
+ StringBuilder sb = new StringBuilder();
+ String separator = "";
+ for (String genre : genres) {
+ sb.append(separator).append(genre);
+ separator = ",";
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Decodes the canonical genre strings from the text stored in the database.
+ *
+ * @param genres The encoded genre string retrieved from the
+ * {@link #COLUMN_CANONICAL_GENRE} column.
+ * @return canonical genre strings.
+ */
+ public static String[] decode(String genres) {
+ return genres.split("\\s*,\\s*");
+ }
+ }
+ }
+
+ /**
+ * Column definitions for the TV programs that the user watched. Applications do not have access
+ * to this table.
+ *
+ * @hide
+ */
+ public static final class WatchedPrograms implements BaseColumns {
+
+ /** The content:// style URI for this table. */
+ public static final Uri CONTENT_URI =
+ Uri.parse("content://" + AUTHORITY + "/watched_program");
+
+ /** The MIME type of a directory of watched programs. */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/watched_program";
+
+ /** The MIME type of a single item in this table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/watched_program";
+
+ /**
+ * The UTC time that the user started watching this TV program, in milliseconds since the
+ * epoch.
+ * <p>
+ * Type: INTEGER (long)
+ * </p>
+ */
+ public static final String COLUMN_WATCH_START_TIME_UTC_MILLIS =
+ "watch_start_time_utc_millis";
+
+ /**
+ * The UTC time that the user stopped watching this TV program, in milliseconds since the
+ * epoch.
+ * <p>
+ * Type: INTEGER (long)
+ * </p>
+ */
+ public static final String COLUMN_WATCH_END_TIME_UTC_MILLIS = "watch_end_time_utc_millis";
+
+ /**
+ * The channel ID that contains this TV program.
+ * <p>
+ * Type: INTEGER (long)
+ * </p>
+ */
+ public static final String COLUMN_CHANNEL_ID = "channel_id";
+
+ /**
+ * The title of this TV program.
+ * <p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_TITLE = "title";
+
+ /**
+ * The start time of this TV program, in milliseconds since the epoch.
+ * <p>
+ * Type: INTEGER (long)
+ * </p>
+ */
+ public static final String COLUMN_START_TIME_UTC_MILLIS = "start_time_utc_millis";
+
+ /**
+ * The end time of this TV program, in milliseconds since the epoch.
+ * <p>
+ * Type: INTEGER (long)
+ * </p>
+ */
+ public static final String COLUMN_END_TIME_UTC_MILLIS = "end_time_utc_millis";
+
+ /**
+ * The description of this TV program.
+ * <p>
+ * Type: TEXT
+ * </p>
+ */
+ public static final String COLUMN_DESCRIPTION = "description";
+
+ private WatchedPrograms() {}
+ }
+}
diff --git a/media/java/android/media/tv/TvInputHardwareInfo.aidl b/media/java/android/media/tv/TvInputHardwareInfo.aidl
new file mode 100644
index 0000000..a4c38bb
--- /dev/null
+++ b/media/java/android/media/tv/TvInputHardwareInfo.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable TvInputHardwareInfo;
diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java
new file mode 100644
index 0000000..4beb960
--- /dev/null
+++ b/media/java/android/media/tv/TvInputHardwareInfo.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Simple container for information about TV input hardware.
+ * Not for third-party developers.
+ *
+ * @hide
+ */
+public final class TvInputHardwareInfo implements Parcelable {
+ static final String TAG = "TvInputHardwareInfo";
+
+ // Match hardware/libhardware/include/hardware/tv_input.h
+ public static final int TV_INPUT_TYPE_HDMI = 1;
+ public static final int TV_INPUT_TYPE_BUILT_IN_TUNER = 2;
+ public static final int TV_INPUT_TYPE_PASSTHROUGH = 3;
+
+ public static final Parcelable.Creator<TvInputHardwareInfo> CREATOR =
+ new Parcelable.Creator<TvInputHardwareInfo>() {
+ @Override
+ public TvInputHardwareInfo createFromParcel(Parcel source) {
+ try {
+ TvInputHardwareInfo info = new TvInputHardwareInfo();
+ info.readFromParcel(source);
+ return info;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvInputHardwareInfo from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvInputHardwareInfo[] newArray(int size) {
+ return new TvInputHardwareInfo[size];
+ }
+ };
+
+ private int mDeviceId;
+ private int mType;
+ // TODO: Add audio port & audio address for audio service.
+ // TODO: Add HDMI handle for HDMI service.
+
+ public TvInputHardwareInfo() { }
+
+ public TvInputHardwareInfo(int deviceId, int type) {
+ mDeviceId = deviceId;
+ mType = type;
+ }
+
+ public int getDeviceId() {
+ return mDeviceId;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDeviceId);
+ dest.writeInt(mType);
+ }
+
+ public void readFromParcel(Parcel source) {
+ mDeviceId = source.readInt();
+ mType = source.readInt();
+ }
+}
diff --git a/media/java/android/media/tv/TvInputInfo.aidl b/media/java/android/media/tv/TvInputInfo.aidl
new file mode 100644
index 0000000..ba139a2
--- /dev/null
+++ b/media/java/android/media/tv/TvInputInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable TvInputInfo;
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
new file mode 100644
index 0000000..7b8f2ec
--- /dev/null
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * This class is used to specify meta information of a TV input.
+ */
+public final class TvInputInfo implements Parcelable {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "TvInputInfo";
+
+ /**
+ * TV input type: the TV input service is not handling input from hardware. For example,
+ * services showing streaming from the internet falls into this type.
+ */
+ public static final int TYPE_VIRTUAL = 0;
+
+ // Should be in sync with hardware/libhardware/include/hardware/tv_input.h
+
+ /**
+ * TV input type: the TV input service is HDMI. (e.g. HDMI 1)
+ */
+ public static final int TYPE_HDMI = 1;
+ /**
+ * TV input type: the TV input service is a tuner. (e.g. terrestrial tuner)
+ */
+ public static final int TYPE_TUNER = 2;
+ /**
+ * TV input type: the TV input service is stateless pass-through. (e.g. RGB, composite, etc.)
+ */
+ public static final int TYPE_PASSTHROUGH = 3;
+
+ /**
+ * The name of the TV input service to provide to the setup activity and settings activity.
+ */
+ public static final String EXTRA_SERVICE_NAME = "serviceName";
+
+ private static final String XML_START_TAG_NAME = "tv-input";
+
+ private final ResolveInfo mService;
+ private final String mId;
+
+ // Attributes from XML meta data.
+ private String mSetupActivity;
+ private String mSettingsActivity;
+ private int mType;
+
+ /**
+ * Create a new instance of the TvInputInfo class,
+ * instantiating it from the given Context and ResolveInfo.
+ *
+ * @param service The ResolveInfo returned from the package manager about this TV input service.
+ * @hide */
+ public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service)
+ throws XmlPullParserException, IOException {
+ ServiceInfo si = service.serviceInfo;
+ PackageManager pm = context.getPackageManager();
+ XmlResourceParser parser = null;
+ try {
+ parser = si.loadXmlMetaData(pm, TvInputService.SERVICE_META_DATA);
+ if (parser == null) {
+ throw new XmlPullParserException("No " + TvInputService.SERVICE_META_DATA
+ + " meta-data for " + si.name);
+ }
+
+ Resources res = pm.getResourcesForApplication(si.applicationInfo);
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!XML_START_TAG_NAME.equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data does not start with tv-input-service tag in " + si.name);
+ }
+
+ TvInputInfo input = new TvInputInfo(context, service);
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.TvInputService);
+ input.mSetupActivity = sa.getString(
+ com.android.internal.R.styleable.TvInputService_setupActivity);
+ if (DEBUG) {
+ Log.d(TAG, "Setup activity loaded. [" + input.mSetupActivity + "] for " + si.name);
+ }
+ input.mSettingsActivity = sa.getString(
+ com.android.internal.R.styleable.TvInputService_settingsActivity);
+ if (DEBUG) {
+ Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
+ + si.name);
+ }
+ input.mType = sa.getInt(
+ com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
+ if (DEBUG) {
+ Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
+ }
+ sa.recycle();
+
+ return input;
+ } catch (NameNotFoundException e) {
+ throw new XmlPullParserException("Unable to create context for: " + si.packageName);
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param service The ResolveInfo returned from the package manager about this TV input service.
+ * @hide
+ */
+ private TvInputInfo(Context context, ResolveInfo service) {
+ mService = service;
+ ServiceInfo si = service.serviceInfo;
+ mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
+ }
+
+ /**
+ * Returns a unique ID for this TV input. The ID is generated from the package and class name
+ * implementing the TV input service.
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the information of the service that implements this TV input.
+ */
+ public ServiceInfo getServiceInfo() {
+ return mService.serviceInfo;
+ }
+
+ /**
+ * Returns the component of the service that implements this TV input.
+ * @hide
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(mService.serviceInfo.packageName, mService.serviceInfo.name);
+ }
+
+ /**
+ * Returns an intent to start the setup activity for this TV input service.
+ */
+ public Intent getIntentForSetupActivity() {
+ if (!TextUtils.isEmpty(mSetupActivity)) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(mService.serviceInfo.packageName, mSetupActivity);
+ intent.putExtra(EXTRA_SERVICE_NAME, mService.serviceInfo.name);
+ return intent;
+ }
+ return null;
+ }
+
+ /**
+ * Returns an intent to start the settings activity for this TV input service.
+ */
+ public Intent getIntentForSettingsActivity() {
+ if (!TextUtils.isEmpty(mSettingsActivity)) {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(mService.serviceInfo.packageName, mSettingsActivity);
+ intent.putExtra(EXTRA_SERVICE_NAME, mService.serviceInfo.name);
+ return intent;
+ }
+ return null;
+ }
+
+ /**
+ * Returns the type of this TV input service.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Loads the user-displayed label for this TV input service.
+ *
+ * @param pm Supplies a PackageManager used to load the TV input's resources.
+ * @return a CharSequence containing the TV input's label. If the TV input does not have
+ * a label, its name is returned.
+ */
+ public CharSequence loadLabel(PackageManager pm) {
+ return mService.loadLabel(pm);
+ }
+
+ /**
+ * Loads the user-displayed icon for this TV input service.
+ *
+ * @param pm Supplies a PackageManager used to load the TV input's resources.
+ * @return a Drawable containing the TV input's icon. If the TV input does not have
+ * an icon, application icon is returned. If it's unavailable too, system default is
+ * returned.
+ */
+ public Drawable loadIcon(PackageManager pm) {
+ return mService.serviceInfo.loadIcon(pm);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return mId.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+
+ if (!(o instanceof TvInputInfo)) {
+ return false;
+ }
+
+ TvInputInfo obj = (TvInputInfo) o;
+ return mId.equals(obj.mId)
+ && mService.serviceInfo.packageName.equals(obj.mService.serviceInfo.packageName)
+ && mService.serviceInfo.name.equals(obj.mService.serviceInfo.name);
+ }
+
+ @Override
+ public String toString() {
+ return "TvInputInfo{id=" + mId
+ + ", pkg=" + mService.serviceInfo.packageName
+ + ", service=" + mService.serviceInfo.name + "}";
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ mService.writeToParcel(dest, flags);
+ dest.writeString(mSetupActivity);
+ dest.writeString(mSettingsActivity);
+ }
+
+ /**
+ * Used to generate an input id from a ComponentName.
+ *
+ * @param name the component name for generating an input id.
+ * @return the generated input id for the given {@code name}.
+ * @hide
+ */
+ public static final String generateInputIdForComponentName(ComponentName name) {
+ return name.flattenToShortString();
+ }
+
+ /**
+ * Used to make this class parcelable.
+ *
+ * @hide
+ */
+ public static final Parcelable.Creator<TvInputInfo> CREATOR =
+ new Parcelable.Creator<TvInputInfo>() {
+ @Override
+ public TvInputInfo createFromParcel(Parcel in) {
+ return new TvInputInfo(in);
+ }
+
+ @Override
+ public TvInputInfo[] newArray(int size) {
+ return new TvInputInfo[size];
+ }
+ };
+
+ private TvInputInfo(Parcel in) {
+ mId = in.readString();
+ mService = ResolveInfo.CREATOR.createFromParcel(in);
+ mSetupActivity = in.readString();
+ mSettingsActivity = in.readString();
+ }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
new file mode 100644
index 0000000..edfdd60
--- /dev/null
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -0,0 +1,924 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pools.Pool;
+import android.util.Pools.SimplePool;
+import android.util.SparseArray;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventSender;
+import android.view.Surface;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Central system API to the overall TV input framework (TIF) architecture, which arbitrates
+ * interaction between applications and the selected TV inputs.
+ */
+public final class TvInputManager {
+ private static final String TAG = "TvInputManager";
+
+ private final ITvInputManager mService;
+
+ // A mapping from an input to the list of its TvInputListenerRecords.
+ private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
+ new HashMap<String, List<TvInputListenerRecord>>();
+
+ // A mapping from the sequence number of a session to its SessionCallbackRecord.
+ private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
+ new SparseArray<SessionCallbackRecord>();
+
+ // A sequence number for the next session to be created. Should be protected by a lock
+ // {@code mSessionCallbackRecordMap}.
+ private int mNextSeq;
+
+ private final ITvInputClient mClient;
+
+ private final int mUserId;
+
+ /**
+ * Interface used to receive the created session.
+ * @hide
+ */
+ public abstract static class SessionCallback {
+ /**
+ * This is called after {@link TvInputManager#createSession} has been processed.
+ *
+ * @param session A {@link TvInputManager.Session} instance created. This can be
+ * {@code null} if the creation request failed.
+ */
+ public void onSessionCreated(Session session) {
+ }
+
+ /**
+ * This is called when {@link TvInputManager.Session} is released.
+ * This typically happens when the process hosting the session has crashed or been killed.
+ *
+ * @param session A {@link TvInputManager.Session} instance released.
+ */
+ public void onSessionReleased(Session session) {
+ }
+
+ /**
+ * This is called at the beginning of the playback of a channel and later when the format of
+ * the video stream has been changed.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback
+ * @param width The width of the video.
+ * @param height The height of the video.
+ * @param interlaced whether the video is interlaced mode or planer mode.
+ * @hide
+ */
+ public void onVideoStreamChanged(Session session, int width, int height,
+ boolean interlaced) {
+ }
+
+ /**
+ * This is called at the beginning of the playback of a channel and later when the format of
+ * the audio stream has been changed.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback
+ * @param channelCount The number of channels in the audio stream.
+ * @hide
+ */
+ public void onAudioStreamChanged(Session session, int channelCount) {
+ }
+
+ /**
+ * This is called at the beginning of the playback of a channel and later when the closed
+ * caption stream has been changed.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback
+ * @param hasClosedCaption Whether the stream has closed caption or not.
+ * @hide
+ */
+ public void onClosedCaptionStreamChanged(Session session, boolean hasClosedCaption) {
+ }
+
+ /**
+ * This is called when a custom event has been sent from this session.
+ *
+ * @param session A {@link TvInputManager.Session} associated with this callback
+ * @param eventType The type of the event.
+ * @param eventArgs Optional arguments of the event.
+ * @hide
+ */
+ public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
+ }
+ }
+
+ private static final class SessionCallbackRecord {
+ private final SessionCallback mSessionCallback;
+ private final Handler mHandler;
+ private Session mSession;
+
+ public SessionCallbackRecord(SessionCallback sessionCallback,
+ Handler handler) {
+ mSessionCallback = sessionCallback;
+ mHandler = handler;
+ }
+
+ public void postSessionCreated(final Session session) {
+ mSession = session;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onSessionCreated(session);
+ }
+ });
+ }
+
+ public void postSessionReleased() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onSessionReleased(mSession);
+ }
+ });
+ }
+
+ public void postVideoStreamChanged(final int width, final int height,
+ final boolean interlaced) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onVideoStreamChanged(mSession, width, height, interlaced);
+ }
+ });
+ }
+
+ public void postAudioStreamChanged(final int channelCount) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onAudioStreamChanged(mSession, channelCount);
+ }
+ });
+ }
+
+ public void postClosedCaptionStreamChanged(final boolean hasClosedCaption) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onClosedCaptionStreamChanged(mSession, hasClosedCaption);
+ }
+ });
+ }
+
+ public void postSessionEvent(final String eventType, final Bundle eventArgs) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onSessionEvent(mSession, eventType, eventArgs);
+ }
+ });
+ }
+ }
+
+ /**
+ * Interface used to monitor status of the TV input.
+ */
+ public abstract static class TvInputListener {
+ /**
+ * This is called when the availability status of a given TV input is changed.
+ *
+ * @param inputId the id of the TV input.
+ * @param isAvailable {@code true} if the given TV input is available to show TV programs.
+ * {@code false} otherwise.
+ */
+ public void onAvailabilityChanged(String inputId, boolean isAvailable) {
+ }
+ }
+
+ private static final class TvInputListenerRecord {
+ private final TvInputListener mListener;
+ private final Handler mHandler;
+
+ public TvInputListenerRecord(TvInputListener listener, Handler handler) {
+ mListener = listener;
+ mHandler = handler;
+ }
+
+ public TvInputListener getListener() {
+ return mListener;
+ }
+
+ public void postAvailabilityChanged(final String inputId, final boolean isAvailable) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onAvailabilityChanged(inputId, isAvailable);
+ }
+ });
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public TvInputManager(ITvInputManager service, int userId) {
+ mService = service;
+ mUserId = userId;
+ mClient = new ITvInputClient.Stub() {
+ @Override
+ public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
+ int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for " + token);
+ return;
+ }
+ Session session = null;
+ if (token != null) {
+ session = new Session(token, channel, mService, mUserId, seq,
+ mSessionCallbackRecordMap);
+ }
+ record.postSessionCreated(session);
+ }
+ }
+
+ @Override
+ public void onSessionReleased(int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ mSessionCallbackRecordMap.delete(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq:" + seq);
+ return;
+ }
+ record.mSession.releaseInternal();
+ record.postSessionReleased();
+ }
+ }
+
+ @Override
+ public void onVideoStreamChanged(int width, int height, boolean interlaced, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postVideoStreamChanged(width, height, interlaced);
+ }
+ }
+
+ @Override
+ public void onAudioStreamChanged(int channelCount, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postAudioStreamChanged(channelCount);
+ }
+ }
+
+ @Override
+ public void onClosedCaptionStreamChanged(boolean hasClosedCaption, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postClosedCaptionStreamChanged(hasClosedCaption);
+ }
+ }
+
+ @Override
+ public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postSessionEvent(eventType, eventArgs);
+ }
+ }
+
+ @Override
+ public void onAvailabilityChanged(String inputId, boolean isAvailable) {
+ synchronized (mTvInputListenerRecordsMap) {
+ List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
+ if (records == null) {
+ // Silently ignore - no listener is registered yet.
+ return;
+ }
+ int recordsCount = records.size();
+ for (int i = 0; i < recordsCount; i++) {
+ records.get(i).postAvailabilityChanged(inputId, isAvailable);
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns the complete list of TV inputs on the system.
+ *
+ * @return List of {@link TvInputInfo} for each TV input that describes its meta information.
+ */
+ public List<TvInputInfo> getTvInputList() {
+ try {
+ return mService.getTvInputList(mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns the availability of a given TV input.
+ *
+ * @param inputId the id of the TV input.
+ * @throws IllegalArgumentException if the argument is {@code null}.
+ * @throws IllegalStateException If there is no {@link TvInputListener} registered on the given
+ * TV input.
+ */
+ public boolean getAvailability(String inputId) {
+ if (inputId == null) {
+ throw new IllegalArgumentException("id cannot be null");
+ }
+ synchronized (mTvInputListenerRecordsMap) {
+ List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
+ if (records == null || records.size() == 0) {
+ throw new IllegalStateException("At least one listener should be registered.");
+ }
+ }
+ try {
+ return mService.getAvailability(mClient, inputId, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Registers a {@link TvInputListener} for a given TV input.
+ *
+ * @param inputId the id of the TV input.
+ * @param listener a listener used to monitor status of the given TV input.
+ * @param handler a {@link Handler} that the status change will be delivered to.
+ * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ * @hide
+ */
+ public void registerListener(String inputId, TvInputListener listener, Handler handler) {
+ if (inputId == null) {
+ throw new IllegalArgumentException("id cannot be null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler cannot be null");
+ }
+ synchronized (mTvInputListenerRecordsMap) {
+ List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
+ if (records == null) {
+ records = new ArrayList<TvInputListenerRecord>();
+ mTvInputListenerRecordsMap.put(inputId, records);
+ try {
+ mService.registerCallback(mClient, inputId, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ records.add(new TvInputListenerRecord(listener, handler));
+ }
+ }
+
+ /**
+ * Unregisters the existing {@link TvInputListener} for a given TV input.
+ *
+ * @param inputId the id of the TV input.
+ * @param listener the existing listener to remove for the given TV input.
+ * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ * @hide
+ */
+ public void unregisterListener(String inputId, final TvInputListener listener) {
+ if (inputId == null) {
+ throw new IllegalArgumentException("id cannot be null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener cannot be null");
+ }
+ synchronized (mTvInputListenerRecordsMap) {
+ List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
+ if (records == null) {
+ Log.e(TAG, "No listener found for " + inputId);
+ return;
+ }
+ for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) {
+ TvInputListenerRecord record = it.next();
+ if (record.getListener() == listener) {
+ it.remove();
+ }
+ }
+ if (records.isEmpty()) {
+ try {
+ mService.unregisterCallback(mClient, inputId, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ mTvInputListenerRecordsMap.remove(inputId);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a {@link Session} for a given TV input.
+ * <p>
+ * The number of sessions that can be created at the same time is limited by the capability of
+ * the given TV input.
+ * </p>
+ *
+ * @param inputId the id of the TV input.
+ * @param callback a callback used to receive the created session.
+ * @param handler a {@link Handler} that the session creation will be delivered to.
+ * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ * @hide
+ */
+ public void createSession(String inputId, final SessionCallback callback,
+ Handler handler) {
+ if (inputId == null) {
+ throw new IllegalArgumentException("id cannot be null");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler cannot be null");
+ }
+ SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
+ synchronized (mSessionCallbackRecordMap) {
+ int seq = mNextSeq++;
+ mSessionCallbackRecordMap.put(seq, record);
+ try {
+ mService.createSession(mClient, inputId, seq, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ /**
+ * The Session provides the per-session functionality of TV inputs.
+ * @hide
+ */
+ public static final class Session {
+ static final int DISPATCH_IN_PROGRESS = -1;
+ static final int DISPATCH_NOT_HANDLED = 0;
+ static final int DISPATCH_HANDLED = 1;
+
+ private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
+
+ private final ITvInputManager mService;
+ private final int mUserId;
+ private final int mSeq;
+
+ // For scheduling input event handling on the main thread. This also serves as a lock to
+ // protect pending input events and the input channel.
+ private final InputEventHandler mHandler = new InputEventHandler(Looper.getMainLooper());
+
+ private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
+ private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
+ private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
+
+ private IBinder mToken;
+ private TvInputEventSender mSender;
+ private InputChannel mChannel;
+
+ /** @hide */
+ private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
+ int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
+ mToken = token;
+ mChannel = channel;
+ mService = service;
+ mUserId = userId;
+ mSeq = seq;
+ mSessionCallbackRecordMap = sessionCallbackRecordMap;
+ }
+
+ /**
+ * Releases this session.
+ *
+ * @throws IllegalStateException if the session has been already released.
+ */
+ public void release() {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.releaseSession(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+
+ releaseInternal();
+ }
+
+ /**
+ * Sets the {@link android.view.Surface} for this session.
+ *
+ * @param surface A {@link android.view.Surface} used to render video.
+ * @throws IllegalStateException if the session has been already released.
+ * @hide
+ */
+ public void setSurface(Surface surface) {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ // surface can be null.
+ try {
+ mService.setSurface(mToken, surface, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Sets the relative stream volume of this session to handle a change of audio focus.
+ *
+ * @param volume A volume value between 0.0f to 1.0f.
+ * @throws IllegalArgumentException if the volume value is out of range.
+ * @throws IllegalStateException if the session has been already released.
+ */
+ public void setStreamVolume(float volume) {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ if (volume < 0.0f || volume > 1.0f) {
+ throw new IllegalArgumentException("volume should be between 0.0f and 1.0f");
+ }
+ mService.setVolume(mToken, volume, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Tunes to a given channel.
+ *
+ * @param channelUri The URI of a channel.
+ * @throws IllegalArgumentException if the argument is {@code null}.
+ * @throws IllegalStateException if the session has been already released.
+ */
+ public void tune(Uri channelUri) {
+ if (channelUri == null) {
+ throw new IllegalArgumentException("channelUri cannot be null");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.tune(mToken, channelUri, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Creates an overlay view. Once the overlay view is created, {@link #relayoutOverlayView}
+ * should be called whenever the layout of its containing view is changed.
+ * {@link #removeOverlayView()} should be called to remove the overlay view.
+ * Since a session can have only one overlay view, this method should be called only once
+ * or it can be called again after calling {@link #removeOverlayView()}.
+ *
+ * @param view A view playing TV.
+ * @param frame A position of the overlay view.
+ * @throws IllegalArgumentException if any of the arguments is {@code null}.
+ * @throws IllegalStateException if {@code view} is not attached to a window or
+ * if the session has been already released.
+ */
+ void createOverlayView(View view, Rect frame) {
+ if (view == null) {
+ throw new IllegalArgumentException("view cannot be null");
+ }
+ if (frame == null) {
+ throw new IllegalArgumentException("frame cannot be null");
+ }
+ if (view.getWindowToken() == null) {
+ throw new IllegalStateException("view must be attached to a window");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Relayouts the current overlay view.
+ *
+ * @param frame A new position of the overlay view.
+ * @throws IllegalArgumentException if the arguments is {@code null}.
+ * @throws IllegalStateException if the session has been already released.
+ */
+ void relayoutOverlayView(Rect frame) {
+ if (frame == null) {
+ throw new IllegalArgumentException("frame cannot be null");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.relayoutOverlayView(mToken, frame, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Removes the current overlay view.
+ *
+ * @throws IllegalStateException if the session has been already released.
+ */
+ void removeOverlayView() {
+ if (mToken == null) {
+ throw new IllegalStateException("the session has been already released");
+ }
+ try {
+ mService.removeOverlayView(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Dispatches an input event to this session.
+ *
+ * @param event {@link InputEvent} to dispatch.
+ * @param token A token used to identify the input event later in the callback.
+ * @param callback A callback used to receive the dispatch result.
+ * @param handler {@link Handler} that the dispatch result will be delivered to.
+ * @return Returns {@link #DISPATCH_HANDLED} if the event was handled. Returns
+ * {@link #DISPATCH_NOT_HANDLED} if the event was not handled. Returns
+ * {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the callback will
+ * be invoked later.
+ * @throws IllegalArgumentException if any of the necessary arguments is {@code null}.
+ * @hide
+ */
+ public int dispatchInputEvent(InputEvent event, Object token,
+ FinishedInputEventCallback callback, Handler handler) {
+ if (event == null) {
+ throw new IllegalArgumentException("event cannot be null");
+ }
+ if (callback != null && handler == null) {
+ throw new IllegalArgumentException("handler cannot be null");
+ }
+ synchronized (mHandler) {
+ if (mChannel == null) {
+ return DISPATCH_NOT_HANDLED;
+ }
+ PendingEvent p = obtainPendingEventLocked(event, token, callback, handler);
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ // Already running on the main thread so we can send the event immediately.
+ return sendInputEventOnMainLooperLocked(p);
+ }
+
+ // Post the event to the main thread.
+ Message msg = mHandler.obtainMessage(InputEventHandler.MSG_SEND_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mHandler.sendMessage(msg);
+ return DISPATCH_IN_PROGRESS;
+ }
+ }
+
+ /**
+ * Callback that is invoked when an input event that was dispatched to this session has been
+ * finished.
+ *
+ * @hide
+ */
+ public interface FinishedInputEventCallback {
+ /**
+ * Called when the dispatched input event is finished.
+ *
+ * @param token a token passed to {@link #dispatchInputEvent}.
+ * @param handled {@code true} if the dispatched input event was handled properly.
+ * {@code false} otherwise.
+ */
+ public void onFinishedInputEvent(Object token, boolean handled);
+ }
+
+ // Must be called on the main looper
+ private void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
+ synchronized (mHandler) {
+ int result = sendInputEventOnMainLooperLocked(p);
+ if (result == DISPATCH_IN_PROGRESS) {
+ return;
+ }
+ }
+
+ invokeFinishedInputEventCallback(p, false);
+ }
+
+ private int sendInputEventOnMainLooperLocked(PendingEvent p) {
+ if (mChannel != null) {
+ if (mSender == null) {
+ mSender = new TvInputEventSender(mChannel, mHandler.getLooper());
+ }
+
+ final InputEvent event = p.mEvent;
+ final int seq = event.getSequenceNumber();
+ if (mSender.sendInputEvent(seq, event)) {
+ mPendingEvents.put(seq, p);
+ Message msg = mHandler.obtainMessage(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, INPUT_SESSION_NOT_RESPONDING_TIMEOUT);
+ return DISPATCH_IN_PROGRESS;
+ }
+
+ Log.w(TAG, "Unable to send input event to session: " + mToken + " dropping:"
+ + event);
+ }
+ return DISPATCH_NOT_HANDLED;
+ }
+
+ void finishedInputEvent(int seq, boolean handled, boolean timeout) {
+ final PendingEvent p;
+ synchronized (mHandler) {
+ int index = mPendingEvents.indexOfKey(seq);
+ if (index < 0) {
+ return; // spurious, event already finished or timed out
+ }
+
+ p = mPendingEvents.valueAt(index);
+ mPendingEvents.removeAt(index);
+
+ if (timeout) {
+ Log.w(TAG, "Timeout waiting for seesion to handle input event after "
+ + INPUT_SESSION_NOT_RESPONDING_TIMEOUT + " ms: " + mToken);
+ } else {
+ mHandler.removeMessages(InputEventHandler.MSG_TIMEOUT_INPUT_EVENT, p);
+ }
+ }
+
+ invokeFinishedInputEventCallback(p, handled);
+ }
+
+ // Assumes the event has already been removed from the queue.
+ void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
+ p.mHandled = handled;
+ if (p.mHandler.getLooper().isCurrentThread()) {
+ // Already running on the callback handler thread so we can send the callback
+ // immediately.
+ p.run();
+ } else {
+ // Post the event to the callback handler thread.
+ // In this case, the callback will be responsible for recycling the event.
+ Message msg = Message.obtain(p.mHandler, p);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ }
+ }
+
+ private void flushPendingEventsLocked() {
+ mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
+
+ final int count = mPendingEvents.size();
+ for (int i = 0; i < count; i++) {
+ int seq = mPendingEvents.keyAt(i);
+ Message msg = mHandler.obtainMessage(InputEventHandler.MSG_FLUSH_INPUT_EVENT, seq, 0);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
+ }
+ }
+
+ private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
+ FinishedInputEventCallback callback, Handler handler) {
+ PendingEvent p = mPendingEventPool.acquire();
+ if (p == null) {
+ p = new PendingEvent();
+ }
+ p.mEvent = event;
+ p.mToken = token;
+ p.mCallback = callback;
+ p.mHandler = handler;
+ return p;
+ }
+
+ private void recyclePendingEventLocked(PendingEvent p) {
+ p.recycle();
+ mPendingEventPool.release(p);
+ }
+
+ private void releaseInternal() {
+ mToken = null;
+ synchronized (mHandler) {
+ if (mChannel != null) {
+ if (mSender != null) {
+ flushPendingEventsLocked();
+ mSender.dispose();
+ mSender = null;
+ }
+ mChannel.dispose();
+ mChannel = null;
+ }
+ }
+ synchronized (mSessionCallbackRecordMap) {
+ mSessionCallbackRecordMap.remove(mSeq);
+ }
+ }
+
+ private final class InputEventHandler extends Handler {
+ public static final int MSG_SEND_INPUT_EVENT = 1;
+ public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
+ public static final int MSG_FLUSH_INPUT_EVENT = 3;
+
+ InputEventHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SEND_INPUT_EVENT: {
+ sendInputEventAndReportResultOnMainLooper((PendingEvent) msg.obj);
+ return;
+ }
+ case MSG_TIMEOUT_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, true);
+ return;
+ }
+ case MSG_FLUSH_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, false);
+ return;
+ }
+ }
+ }
+ }
+
+ private final class TvInputEventSender extends InputEventSender {
+ public TvInputEventSender(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ @Override
+ public void onInputEventFinished(int seq, boolean handled) {
+ finishedInputEvent(seq, handled, false);
+ }
+ }
+
+ private final class PendingEvent implements Runnable {
+ public InputEvent mEvent;
+ public Object mToken;
+ public FinishedInputEventCallback mCallback;
+ public Handler mHandler;
+ public boolean mHandled;
+
+ public void recycle() {
+ mEvent = null;
+ mToken = null;
+ mCallback = null;
+ mHandler = null;
+ mHandled = false;
+ }
+
+ @Override
+ public void run() {
+ mCallback.onFinishedInputEvent(mToken, mHandled);
+
+ synchronized (mHandler) {
+ recyclePendingEventLocked(this);
+ }
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
new file mode 100644
index 0000000..409a33c
--- /dev/null
+++ b/media/java/android/media/tv/TvInputService.java
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
+
+/**
+ * The TvInputService class represents a TV input or source such as HDMI or built-in tuner which
+ * provides pass-through video or broadcast TV programs.
+ * <p>
+ * Applications will not normally use this service themselves, instead relying on the standard
+ * interaction provided by {@link TvView}. Those implementing TV input services should normally do
+ * so by deriving from this class and providing their own session implementation based on
+ * {@link TvInputService.Session}. All TV input services must require that clients hold the
+ * {@link android.Manifest.permission#BIND_TV_INPUT} in order to interact with the service; if this
+ * permission is not specified in the manifest, the system will refuse to bind to that TV input
+ * service.
+ * </p>
+ */
+public abstract class TvInputService extends Service {
+ // STOPSHIP: Turn debugging off.
+ private static final boolean DEBUG = true;
+ private static final String TAG = "TvInputService";
+
+ /**
+ * This is the interface name that a service implementing a TV input should say that it support
+ * -- that is, this is the action it uses for its intent filter. To be supported, the service
+ * must also require the {@link android.Manifest.permission#BIND_TV_INPUT} permission so that
+ * other applications cannot abuse it.
+ */
+ public static final String SERVICE_INTERFACE = "android.media.tv.TvInputService";
+
+ /**
+ * Name under which a TvInputService component publishes information about itself.
+ * This meta-data must reference an XML resource containing an
+ * <code><{@link android.R.styleable#TvInputService tv-input}></code>
+ * tag.
+ */
+ public static final String SERVICE_META_DATA = "android.media.tv.input";
+
+ private String mId;
+ private final Handler mHandler = new ServiceHandler();
+ private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
+ new RemoteCallbackList<ITvInputServiceCallback>();
+ // STOPSHIP: Redesign the API around the availability change. For now, the service will be
+ // always available.
+ private final boolean mAvailable = true;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mId = TvInputInfo.generateInputIdForComponentName(
+ new ComponentName(getPackageName(), getClass().getName()));
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new ITvInputService.Stub() {
+ @Override
+ public void registerCallback(ITvInputServiceCallback cb) {
+ if (cb != null) {
+ mCallbacks.register(cb);
+ // The first time a callback is registered, the service needs to report its
+ // availability status so that the system can know its initial value.
+ try {
+ cb.onAvailabilityChanged(mId, mAvailable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in onAvailabilityChanged", e);
+ }
+ }
+ }
+
+ @Override
+ public void unregisterCallback(ITvInputServiceCallback cb) {
+ if (cb != null) {
+ mCallbacks.unregister(cb);
+ }
+ }
+
+ @Override
+ public void createSession(InputChannel channel, ITvInputSessionCallback cb) {
+ if (channel == null) {
+ Log.w(TAG, "Creating session without input channel");
+ }
+ if (cb == null) {
+ return;
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = channel;
+ args.arg2 = cb;
+ mHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args).sendToTarget();
+ }
+ };
+ }
+
+ /**
+ * Get the number of callbacks that are registered.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public final int getRegisteredCallbackCount() {
+ return mCallbacks.getRegisteredCallbackCount();
+ }
+
+ /**
+ * Returns a concrete implementation of {@link Session}.
+ * <p>
+ * May return {@code null} if this TV input service fails to create a session for some reason.
+ * </p>
+ */
+ public abstract Session onCreateSession();
+
+ /**
+ * Base class for derived classes to implement to provide a TV input session.
+ */
+ public abstract class Session implements KeyEvent.Callback {
+ private final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
+ private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mWindowParams;
+ private Surface mSurface;
+ private View mOverlayView;
+ private boolean mOverlayViewEnabled;
+ private IBinder mWindowToken;
+ private Rect mOverlayFrame;
+ private ITvInputSessionCallback mSessionCallback;
+
+ public Session() {
+ mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+ }
+
+ /**
+ * Enables or disables the overlay view. By default, the overlay view is disabled. Must be
+ * called explicitly after the session is created to enable the overlay view.
+ *
+ * @param enable {@code true} if you want to enable the overlay view. {@code false}
+ * otherwise.
+ */
+ public void setOverlayViewEnabled(final boolean enable) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (enable == mOverlayViewEnabled) {
+ return;
+ }
+ mOverlayViewEnabled = enable;
+ if (enable) {
+ if (mWindowToken != null) {
+ createOverlayView(mWindowToken, mOverlayFrame);
+ }
+ } else {
+ removeOverlayView(false);
+ }
+ }
+ });
+ }
+
+ /**
+ * Dispatches an event to the application using this session.
+ *
+ * @param eventType The type of the event.
+ * @param eventArgs Optional arguments of the event.
+ * @hide
+ */
+ public void dispatchSessionEvent(final String eventType, final Bundle eventArgs) {
+ if (eventType == null) {
+ throw new IllegalArgumentException("eventType should not be null.");
+ }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "dispatchSessionEvent(" + eventType + ")");
+ mSessionCallback.onSessionEvent(eventType, eventArgs);
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in sending event (event=" + eventType + ")");
+ }
+ }
+ });
+ }
+
+ /**
+ * Sends the change on the format of the video stream. This is expected to be called at the
+ * beginning of the playback and later when the format has been changed.
+ *
+ * @param width The width of the video.
+ * @param height The height of the video.
+ * @param interlaced Whether the video is interlaced mode or planer mode.
+ * @hide
+ */
+ public void dispatchVideoStreamChanged(final int width, final int height,
+ final boolean interlaced) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "dispatchVideoSizeChanged");
+ mSessionCallback.onVideoStreamChanged(width, height, interlaced);
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in dispatchVideoSizeChanged");
+ }
+ }
+ });
+ }
+
+ /**
+ * Sends the change on the format of the audio stream. This is expected to be called at the
+ * beginning of the playback and later when the format has been changed.
+ *
+ * @param channelNumber The number of channels in the audio stream.
+ * @hide
+ */
+ public void dispatchAudioStreamChanged(final int channelNumber) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "dispatchAudioStreamChanged");
+ mSessionCallback.onAudioStreamChanged(channelNumber);
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in dispatchAudioStreamChanged");
+ }
+ }
+ });
+ }
+
+ /**
+ * Sends the change on the closed caption stream. This is expected to be called at the
+ * beginning of the playback and later when the stream has been changed.
+ *
+ * @param hasClosedCaption Whether the stream has closed caption or not.
+ * @hide
+ */
+ public void dispatchClosedCaptionStreamChanged(final boolean hasClosedCaption) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "dispatchClosedCaptionStreamChanged");
+ mSessionCallback.onClosedCaptionStreamChanged(hasClosedCaption);
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in dispatchClosedCaptionStreamChanged");
+ }
+ }
+ });
+ }
+
+ /**
+ * Called when the session is released.
+ */
+ public abstract void onRelease();
+
+ /**
+ * Sets the {@link Surface} for the current input session on which the TV input renders
+ * video.
+ *
+ * @param surface {@link Surface} an application passes to this TV input session.
+ * @return {@code true} if the surface was set, {@code false} otherwise.
+ */
+ public abstract boolean onSetSurface(Surface surface);
+
+ /**
+ * Sets the relative stream volume of the current TV input session to handle the change of
+ * audio focus by setting.
+ *
+ * @param volume Volume scale from 0.0 to 1.0.
+ */
+ public abstract void onSetStreamVolume(float volume);
+
+ /**
+ * Tunes to a given channel.
+ *
+ * @param channelUri The URI of the channel.
+ * @return {@code true} the tuning was successful, {@code false} otherwise.
+ */
+ public abstract boolean onTune(Uri channelUri);
+
+ /**
+ * Called when an application requests to create an overlay view. Each session
+ * implementation can override this method and return its own view.
+ *
+ * @return a view attached to the overlay window
+ */
+ public View onCreateOverlayView() {
+ return null;
+ }
+
+ /**
+ * Default implementation of {@link android.view.KeyEvent.Callback#onKeyDown(int, KeyEvent)
+ * KeyEvent.Callback.onKeyDown()}: always returns false (doesn't handle the event).
+ * <p>
+ * Override this to intercept key down events before they are processed by the application.
+ * If you return true, the application will not process the event itself. If you return
+ * false, the normal application processing will occur as if the TV input had not seen the
+ * event at all.
+ *
+ * @param keyCode The value in event.getKeyCode().
+ * @param event Description of the key event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
+ * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle the event).
+ * <p>
+ * Override this to intercept key long press events before they are processed by the
+ * application. If you return true, the application will not process the event itself. If
+ * you return false, the normal application processing will occur as if the TV input had not
+ * seen the event at all.
+ *
+ * @param keyCode The value in event.getKeyCode().
+ * @param event Description of the key event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Default implementation of
+ * {@link android.view.KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
+ * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle the event).
+ * <p>
+ * Override this to intercept special key multiple events before they are processed by the
+ * application. If you return true, the application will not itself process the event. If
+ * you return false, the normal application processing will occur as if the TV input had not
+ * seen the event at all.
+ *
+ * @param keyCode The value in event.getKeyCode().
+ * @param count The number of times the action was made.
+ * @param event Description of the key event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ @Override
+ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Default implementation of {@link android.view.KeyEvent.Callback#onKeyUp(int, KeyEvent)
+ * KeyEvent.Callback.onKeyUp()}: always returns false (doesn't handle the event).
+ * <p>
+ * Override this to intercept key up events before they are processed by the application. If
+ * you return true, the application will not itself process the event. If you return false,
+ * the normal application processing will occur as if the TV input had not seen the event at
+ * all.
+ *
+ * @param keyCode The value in event.getKeyCode().
+ * @param event Description of the key event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Implement this method to handle touch screen motion events on the current input session.
+ *
+ * @param event The motion event being received.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ * @see View#onTouchEvent
+ */
+ public boolean onTouchEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * Implement this method to handle trackball events on the current input session.
+ *
+ * @param event The motion event being received.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ * @see View#onTrackballEvent
+ */
+ public boolean onTrackballEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * Implement this method to handle generic motion events on the current input session.
+ *
+ * @param event The motion event being received.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ * @see View#onGenericMotionEvent
+ */
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * This method is called when the application would like to stop using the current input
+ * session.
+ */
+ void release() {
+ onRelease();
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
+ removeOverlayView(true);
+ }
+
+ /**
+ * Calls {@link #onSetSurface}.
+ */
+ void setSurface(Surface surface) {
+ onSetSurface(surface);
+ if (mSurface != null) {
+ mSurface.release();
+ }
+ mSurface = surface;
+ // TODO: Handle failure.
+ }
+
+ /**
+ * Calls {@link #onSetStreamVolume}.
+ */
+ void setVolume(float volume) {
+ onSetStreamVolume(volume);
+ }
+
+ /**
+ * Calls {@link #onTune}.
+ */
+ void tune(Uri channelUri) {
+ onTune(channelUri);
+ // TODO: Handle failure.
+ }
+
+ /**
+ * Creates an overlay view. This calls {@link #onCreateOverlayView} to get a view to attach
+ * to the overlay window.
+ *
+ * @param windowToken A window token of an application.
+ * @param frame A position of the overlay view.
+ */
+ void createOverlayView(IBinder windowToken, Rect frame) {
+ if (mOverlayView != null) {
+ mWindowManager.removeView(mOverlayView);
+ mOverlayView = null;
+ }
+ if (DEBUG) Log.d(TAG, "create overlay view(" + frame + ")");
+ mWindowToken = windowToken;
+ mOverlayFrame = frame;
+ if (!mOverlayViewEnabled) {
+ return;
+ }
+ mOverlayView = onCreateOverlayView();
+ if (mOverlayView == null) {
+ return;
+ }
+ // TvView's window type is TYPE_APPLICATION_MEDIA and we want to create
+ // an overlay window above the media window but below the application window.
+ int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
+ // We make the overlay view non-focusable and non-touchable so that
+ // the application that owns the window token can decide whether to consume or
+ // dispatch the input events.
+ int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ mWindowParams = new WindowManager.LayoutParams(
+ frame.right - frame.left, frame.bottom - frame.top,
+ frame.left, frame.top, type, flag, PixelFormat.TRANSPARENT);
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ mWindowParams.gravity = Gravity.START | Gravity.TOP;
+ mWindowParams.token = windowToken;
+ mWindowManager.addView(mOverlayView, mWindowParams);
+ }
+
+ /**
+ * Relayouts the current overlay view.
+ *
+ * @param frame A new position of the overlay view.
+ */
+ void relayoutOverlayView(Rect frame) {
+ if (DEBUG) Log.d(TAG, "relayoutOverlayView(" + frame + ")");
+ mOverlayFrame = frame;
+ if (!mOverlayViewEnabled || mOverlayView == null) {
+ return;
+ }
+ mWindowParams.x = frame.left;
+ mWindowParams.y = frame.top;
+ mWindowParams.width = frame.right - frame.left;
+ mWindowParams.height = frame.bottom - frame.top;
+ mWindowManager.updateViewLayout(mOverlayView, mWindowParams);
+ }
+
+ /**
+ * Removes the current overlay view.
+ */
+ void removeOverlayView(boolean clearWindowToken) {
+ if (DEBUG) Log.d(TAG, "removeOverlayView(" + mOverlayView + ")");
+ if (clearWindowToken) {
+ mWindowToken = null;
+ mOverlayFrame = null;
+ }
+ if (mOverlayView != null) {
+ mWindowManager.removeView(mOverlayView);
+ mOverlayView = null;
+ mWindowParams = null;
+ }
+ }
+
+ /**
+ * Takes care of dispatching incoming input events and tells whether the event was handled.
+ */
+ int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
+ if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
+ if (event instanceof KeyEvent) {
+ if (((KeyEvent) event).dispatch(this, mDispatcherState, this)) {
+ return TvInputManager.Session.DISPATCH_HANDLED;
+ }
+ } else if (event instanceof MotionEvent) {
+ MotionEvent motionEvent = (MotionEvent) event;
+ final int source = motionEvent.getSource();
+ if (motionEvent.isTouchEvent()) {
+ if (onTouchEvent(motionEvent)) {
+ return TvInputManager.Session.DISPATCH_HANDLED;
+ }
+ } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ if (onTrackballEvent(motionEvent)) {
+ return TvInputManager.Session.DISPATCH_HANDLED;
+ }
+ } else {
+ if (onGenericMotionEvent(motionEvent)) {
+ return TvInputManager.Session.DISPATCH_HANDLED;
+ }
+ }
+ }
+ if (mOverlayView == null || !mOverlayView.isAttachedToWindow()) {
+ return TvInputManager.Session.DISPATCH_NOT_HANDLED;
+ }
+ if (!mOverlayView.hasWindowFocus()) {
+ mOverlayView.getViewRootImpl().windowFocusChanged(true, true);
+ }
+ mOverlayView.getViewRootImpl().dispatchInputEvent(event, receiver);
+ return TvInputManager.Session.DISPATCH_IN_PROGRESS;
+ }
+
+ private void setSessionCallback(ITvInputSessionCallback callback) {
+ mSessionCallback = callback;
+ }
+ }
+
+ private final class ServiceHandler extends Handler {
+ private static final int DO_CREATE_SESSION = 1;
+ private static final int DO_BROADCAST_AVAILABILITY_CHANGE = 2;
+
+ @Override
+ public final void handleMessage(Message msg) {
+ switch (msg.what) {
+ case DO_CREATE_SESSION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ InputChannel channel = (InputChannel) args.arg1;
+ ITvInputSessionCallback cb = (ITvInputSessionCallback) args.arg2;
+ try {
+ Session sessionImpl = onCreateSession();
+ if (sessionImpl == null) {
+ // Failed to create a session.
+ cb.onSessionCreated(null);
+ } else {
+ sessionImpl.setSessionCallback(cb);
+ ITvInputSession stub = new ITvInputSessionWrapper(TvInputService.this,
+ sessionImpl, channel);
+ cb.onSessionCreated(stub);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "error in onSessionCreated");
+ }
+ args.recycle();
+ return;
+ }
+ case DO_BROADCAST_AVAILABILITY_CHANGE: {
+ boolean isAvailable = (Boolean) msg.obj;
+ int n = mCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < n; i++) {
+ mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unexpected exception", e);
+ } finally {
+ mCallbacks.finishBroadcast();
+ }
+ return;
+ }
+ default: {
+ Log.w(TAG, "Unhandled message code: " + msg.what);
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/media/java/android/media/tv/TvStreamConfig.aidl b/media/java/android/media/tv/TvStreamConfig.aidl
new file mode 100644
index 0000000..569fcc0
--- /dev/null
+++ b/media/java/android/media/tv/TvStreamConfig.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable TvStreamConfig;
\ No newline at end of file
diff --git a/media/java/android/media/tv/TvStreamConfig.java b/media/java/android/media/tv/TvStreamConfig.java
new file mode 100644
index 0000000..7f0c92f
--- /dev/null
+++ b/media/java/android/media/tv/TvStreamConfig.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TvStreamConfig implements Parcelable {
+ static final String TAG = TvStreamConfig.class.getSimpleName();
+
+ public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1;
+ public final static int STREAM_TYPE_BUFFER_PRODUCER = 2;
+
+ private int mStreamId;
+ private int mType;
+ // TODO: Revisit if max widht/height really make sense.
+ private int mMaxWidth;
+ private int mMaxHeight;
+ /**
+ * Generations are incremented once framework receives STREAM_CONFIGURATION_CHANGED event from
+ * HAL module. Framework should throw away outdated configurations and get new configurations
+ * via tv_input_device::get_stream_configurations().
+ */
+ private int mGeneration;
+
+ public static final Parcelable.Creator<TvStreamConfig> CREATOR =
+ new Parcelable.Creator<TvStreamConfig>() {
+ @Override
+ public TvStreamConfig createFromParcel(Parcel source) {
+ try {
+ return new Builder().
+ streamId(source.readInt()).
+ type(source.readInt()).
+ maxWidth(source.readInt()).
+ maxHeight(source.readInt()).
+ generation(source.readInt()).build();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvStreamConfig from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvStreamConfig[] newArray(int size) {
+ return new TvStreamConfig[size];
+ }
+ };
+
+ private TvStreamConfig() {}
+
+ public int getStreamId() {
+ return mStreamId;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
+ public int getMaxHeight() {
+ return mMaxHeight;
+ }
+
+ public int getGeneration() {
+ return mGeneration;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mStreamId);
+ dest.writeInt(mType);
+ dest.writeInt(mMaxWidth);
+ dest.writeInt(mMaxHeight);
+ dest.writeInt(mGeneration);
+ }
+
+ /**
+ * A helper class for creating a TvStreamConfig object.
+ */
+ public static final class Builder {
+ private Integer mStreamId;
+ private Integer mType;
+ private Integer mMaxWidth;
+ private Integer mMaxHeight;
+ private Integer mGeneration;
+
+ public Builder() {
+ }
+
+ public Builder streamId(int streamId) {
+ mStreamId = streamId;
+ return this;
+ }
+
+ public Builder type(int type) {
+ mType = type;
+ return this;
+ }
+
+ public Builder maxWidth(int maxWidth) {
+ mMaxWidth = maxWidth;
+ return this;
+ }
+
+ public Builder maxHeight(int maxHeight) {
+ mMaxHeight = maxHeight;
+ return this;
+ }
+
+ public Builder generation(int generation) {
+ mGeneration = generation;
+ return this;
+ }
+
+ public TvStreamConfig build() {
+ if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null
+ || mGeneration == null) {
+ throw new UnsupportedOperationException();
+ }
+
+ TvStreamConfig config = new TvStreamConfig();
+ config.mStreamId = mStreamId;
+ config.mType = mType;
+ config.mMaxWidth = mMaxWidth;
+ config.mMaxHeight = mMaxHeight;
+ config.mGeneration = mGeneration;
+ return config;
+ }
+ }
+}
\ No newline at end of file
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
new file mode 100644
index 0000000..2831d9e
--- /dev/null
+++ b/media/java/android/media/tv/TvView.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.media.tv.TvInputManager.Session;
+import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
+import android.media.tv.TvInputManager.SessionCallback;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.view.ViewRootImpl;
+
+/**
+ * View playing TV
+ */
+public class TvView extends ViewGroup {
+ private static final String TAG = "TvView";
+ // STOPSHIP: Turn debugging off.
+ private static final boolean DEBUG = true;
+
+ /**
+ * Passed with {@link TvInputListener#onError(String, int)}. Indicates that the requested TV
+ * input is busy and unable to handle the request.
+ */
+ public static final int ERROR_BUSY = 0;
+
+ /**
+ * Passed with {@link TvInputListener#onError(String, int)}. Indicates that the underlying TV
+ * input has been disconnected.
+ */
+ public static final int ERROR_TV_INPUT_DISCONNECTED = 1;
+
+ private final Handler mHandler = new Handler();
+ private TvInputManager.Session mSession;
+ private final SurfaceView mSurfaceView;
+ private Surface mSurface;
+ private boolean mOverlayViewCreated;
+ private Rect mOverlayViewFrame;
+ private final TvInputManager mTvInputManager;
+ private MySessionCallback mSessionCallback;
+ private TvInputListener mListener;
+ private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
+ private boolean mHasStreamVolume;
+ private float mStreamVolume;
+
+ private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Log.d(TAG, "surfaceChanged(holder=" + holder + ", format=" + format + ", width=" + width
+ + ", height=" + height + ")");
+ if (holder.getSurface() == mSurface) {
+ return;
+ }
+ mSurface = holder.getSurface();
+ setSessionSurface(mSurface);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ mSurface = holder.getSurface();
+ setSessionSurface(mSurface);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mSurface = null;
+ setSessionSurface(null);
+ }
+ };
+
+ private final FinishedInputEventCallback mFinishedInputEventCallback =
+ new FinishedInputEventCallback() {
+ @Override
+ public void onFinishedInputEvent(Object token, boolean handled) {
+ if (DEBUG) {
+ Log.d(TAG, "onFinishedInputEvent(token=" + token + ", handled=" + handled + ")");
+ }
+ if (handled) {
+ return;
+ }
+ // TODO: Re-order unhandled events.
+ InputEvent event = (InputEvent) token;
+ if (dispatchUnhandledInputEvent(event)) {
+ return;
+ }
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.dispatchUnhandledInputEvent(event);
+ }
+ }
+ };
+
+ public TvView(Context context) {
+ this(context, null, 0);
+ }
+
+ public TvView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TvView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mSurfaceView = new SurfaceView(context, attrs, defStyleAttr) {
+ @Override
+ protected void updateWindow(boolean force, boolean redrawNeeded) {
+ super.updateWindow(force, redrawNeeded);
+ relayoutSessionOverlayView();
+ }};
+ mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ addView(mSurfaceView);
+ mTvInputManager = (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+ }
+
+ /**
+ * Sets a listener for events in this TvView.
+ *
+ * @param listener The listener to be called with events. A value of {@code null} removes any
+ * existing listener.
+ */
+ public void setTvInputListener(TvInputListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Sets the relative stream volume of this session to handle a change of audio focus.
+ *
+ * @param volume A volume value between 0.0f to 1.0f.
+ */
+ public void setStreamVolume(float volume) {
+ if (DEBUG) Log.d(TAG, "setStreamVolume(" + volume + ")");
+ mHasStreamVolume = true;
+ mStreamVolume = volume;
+ if (mSession == null) {
+ // Volume will be set once the connection has been made.
+ return;
+ }
+ mSession.setStreamVolume(volume);
+ }
+
+ /**
+ * Tunes to a given channel.
+ *
+ * @param inputId the id of TV input which will play the given channel.
+ * @param channelUri The URI of a channel.
+ */
+ public void tune(String inputId, Uri channelUri) {
+ if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")");
+ if (TextUtils.isEmpty(inputId)) {
+ throw new IllegalArgumentException("inputId cannot be null or an empty string");
+ }
+ if (mSessionCallback != null && mSessionCallback.mInputId.equals(inputId)) {
+ if (mSession != null) {
+ mSession.tune(channelUri);
+ } else {
+ // Session is not created yet. Replace the channel which will be set once the
+ // session is made.
+ mSessionCallback.mChannelUri = channelUri;
+ }
+ } else {
+ if (mSession != null) {
+ release();
+ }
+ // When createSession() is called multiple times before the callback is called,
+ // only the callback of the last createSession() call will be actually called back.
+ // The previous callbacks will be ignored. For the logic, mSessionCallback
+ // is newly assigned for every createSession request and compared with
+ // MySessionCreateCallback.this.
+ mSessionCallback = new MySessionCallback(inputId, channelUri);
+ mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
+ }
+ }
+
+ /**
+ * Resets this TvView.
+ * <p>
+ * This method is primarily used to un-tune the current TvView.
+ */
+ public void reset() {
+ if (mSession != null) {
+ release();
+ }
+ }
+
+ /**
+ * Dispatches an unhandled input event to the next receiver.
+ * <p>
+ * Except system keys, TvView always consumes input events in the normal flow. This is called
+ * asynchronously from where the event is dispatched. It gives the host application a chance to
+ * dispatch the unhandled input events.
+ *
+ * @param event The input event.
+ * @return {@code true} if the event was handled by the view, {@code false} otherwise.
+ */
+ public boolean dispatchUnhandledInputEvent(InputEvent event) {
+ if (mOnUnhandledInputEventListener != null) {
+ if (mOnUnhandledInputEventListener.onUnhandledInputEvent(event)) {
+ return true;
+ }
+ }
+ return onUnhandledInputEvent(event);
+ }
+
+ /**
+ * Called when an unhandled input event was also not handled by the user provided callback. This
+ * is the last chance to handle the unhandled input event in the TvView.
+ *
+ * @param event The input event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to be
+ * handled by the next receiver, return {@code false}.
+ */
+ public boolean onUnhandledInputEvent(InputEvent event) {
+ return false;
+ }
+
+ /**
+ * Registers a callback to be invoked when an input event was not handled by the bound TV input.
+ *
+ * @param listener The callback to invoke when the unhandled input event was received.
+ */
+ public void setOnUnhandledInputEventListener(OnUnhandledInputEventListener listener) {
+ mOnUnhandledInputEventListener = listener;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (super.dispatchKeyEvent(event)) {
+ return true;
+ }
+ if (DEBUG) Log.d(TAG, "dispatchKeyEvent(" + event + ")");
+ if (mSession == null) {
+ return false;
+ }
+ InputEvent copiedEvent = event.copy();
+ int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+ mHandler);
+ return ret != Session.DISPATCH_NOT_HANDLED;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (super.dispatchTouchEvent(event)) {
+ return true;
+ }
+ if (DEBUG) Log.d(TAG, "dispatchTouchEvent(" + event + ")");
+ if (mSession == null) {
+ return false;
+ }
+ InputEvent copiedEvent = event.copy();
+ int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+ mHandler);
+ return ret != Session.DISPATCH_NOT_HANDLED;
+ }
+
+ @Override
+ public boolean dispatchTrackballEvent(MotionEvent event) {
+ if (super.dispatchTrackballEvent(event)) {
+ return true;
+ }
+ if (DEBUG) Log.d(TAG, "dispatchTrackballEvent(" + event + ")");
+ if (mSession == null) {
+ return false;
+ }
+ InputEvent copiedEvent = event.copy();
+ int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+ mHandler);
+ return ret != Session.DISPATCH_NOT_HANDLED;
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent event) {
+ if (super.dispatchGenericMotionEvent(event)) {
+ return true;
+ }
+ if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent(" + event + ")");
+ if (mSession == null) {
+ return false;
+ }
+ InputEvent copiedEvent = event.copy();
+ int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+ mHandler);
+ return ret != Session.DISPATCH_NOT_HANDLED;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ createSessionOverlayView();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ removeSessionOverlayView();
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mSurfaceView.layout(0, 0, right - left, bottom - top);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mSurfaceView.measure(widthMeasureSpec, heightMeasureSpec);
+ int width = mSurfaceView.getMeasuredWidth();
+ int height = mSurfaceView.getMeasuredHeight();
+ int childState = mSurfaceView.getMeasuredState();
+ setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
+ resolveSizeAndState(height, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ mSurfaceView.setVisibility(visibility);
+ }
+
+ private void release() {
+ setSessionSurface(null);
+ removeSessionOverlayView();
+ mSession.release();
+ mSession = null;
+ mSessionCallback = null;
+ }
+
+ private void setSessionSurface(Surface surface) {
+ if (mSession == null) {
+ return;
+ }
+ mSession.setSurface(surface);
+ }
+
+ private void createSessionOverlayView() {
+ if (mSession == null || !isAttachedToWindow()
+ || mOverlayViewCreated) {
+ return;
+ }
+ mOverlayViewFrame = getViewFrameOnScreen();
+ mSession.createOverlayView(this, mOverlayViewFrame);
+ mOverlayViewCreated = true;
+ }
+
+ private void removeSessionOverlayView() {
+ if (mSession == null || !mOverlayViewCreated) {
+ return;
+ }
+ mSession.removeOverlayView();
+ mOverlayViewCreated = false;
+ mOverlayViewFrame = null;
+ }
+
+ private void relayoutSessionOverlayView() {
+ if (mSession == null || !isAttachedToWindow()
+ || !mOverlayViewCreated) {
+ return;
+ }
+ Rect viewFrame = getViewFrameOnScreen();
+ if (viewFrame.equals(mOverlayViewFrame)) {
+ return;
+ }
+ mSession.relayoutOverlayView(viewFrame);
+ mOverlayViewFrame = viewFrame;
+ }
+
+ private Rect getViewFrameOnScreen() {
+ int[] location = new int[2];
+ getLocationOnScreen(location);
+ return new Rect(location[0], location[1],
+ location[0] + getWidth(), location[1] + getHeight());
+ }
+
+ /**
+ * Interface used to receive various status updates on the {@link TvView}.
+ */
+ public abstract static class TvInputListener {
+
+ /**
+ * This is invoked when an error occurred while handling requested operation.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param errorCode The error code. For the details of error code, please see
+ * {@link TvView}.
+ */
+ public void onError(String inputId, int errorCode) {
+ }
+
+ /**
+ * This is invoked when the view is tuned to a specific channel and starts decoding video
+ * stream from there. It is also called later when the video format is changed.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param width The width of the video.
+ * @param height The height of the video.
+ * @param interlaced {@code true} if the video is interlaced, {@code false} if the video is
+ * progressive.
+ * @hide
+ */
+ public void onVideoStreamChanged(String inputId, int width, int height,
+ boolean interlaced) {
+ }
+
+ /**
+ * This is invoked when the view is tuned to a specific channel and starts decoding audio
+ * stream from there. It is also called later when the audio format is changed.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param channelCount The number of channels in the audio stream.
+ * @hide
+ */
+ public void onAudioStreamChanged(String inputId, int channelCount) {
+ }
+
+ /**
+ * This is invoked when the view is tuned to a specific channel and starts decoding data
+ * stream that includes subtitle information from the channel. It is also called later when
+ * the information disappears or appears.
+ *
+ * @param inputId The ID of the TV input bound to this view.
+ * @param hasClosedCaption {@code true} if the stream contains closed caption, {@code false}
+ * otherwise.
+ * @hide
+ */
+ public void onClosedCaptionStreamChanged(String inputId, boolean hasClosedCaption) {
+ }
+
+ /**
+ * This is invoked when a custom event from the bound TV input is sent to this view.
+ *
+ * @param eventType The type of the event.
+ * @param eventArgs Optional arguments of the event.
+ * @hide
+ */
+ public void onEvent(String inputId, String eventType, Bundle eventArgs) {
+ }
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the unhandled input event is received.
+ */
+ public interface OnUnhandledInputEventListener {
+ /**
+ * Called when an input event was not handled by the bound TV input.
+ * <p>
+ * This is called asynchronously from where the event is dispatched. It gives the host
+ * application a chance to handle the unhandled input events.
+ *
+ * @param event The input event.
+ * @return If you handled the event, return {@code true}. If you want to allow the event to
+ * be handled by the next receiver, return {@code false}.
+ */
+ boolean onUnhandledInputEvent(InputEvent event);
+ }
+
+ private class MySessionCallback extends SessionCallback {
+ final String mInputId;
+ Uri mChannelUri;
+
+ MySessionCallback(String inputId, Uri channelUri) {
+ mInputId = inputId;
+ mChannelUri = channelUri;
+ }
+
+ @Override
+ public void onSessionCreated(Session session) {
+ if (this != mSessionCallback) {
+ // This callback is obsolete.
+ if (session != null) {
+ session.release();
+ }
+ return;
+ }
+ mSession = session;
+ if (session != null) {
+ // mSurface may not be ready yet as soon as starting an application.
+ // In the case, we don't send Session.setSurface(null) unnecessarily.
+ // setSessionSurface will be called in surfaceCreated.
+ if (mSurface != null) {
+ setSessionSurface(mSurface);
+ }
+ createSessionOverlayView();
+ mSession.tune(mChannelUri);
+ if (mHasStreamVolume) {
+ mSession.setStreamVolume(mStreamVolume);
+ }
+ } else {
+ if (mListener != null) {
+ mListener.onError(mInputId, ERROR_BUSY);
+ }
+ }
+ }
+
+ @Override
+ public void onSessionReleased(Session session) {
+ mSession = null;
+ mSessionCallback = null;
+ if (mListener != null) {
+ mListener.onError(mInputId, ERROR_TV_INPUT_DISCONNECTED);
+ }
+ }
+
+ @Override
+ public void onVideoStreamChanged(Session session, int width, int height,
+ boolean interlaced) {
+ if (DEBUG) {
+ Log.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
+ }
+ if (mListener != null) {
+ mListener.onVideoStreamChanged(mInputId, width, height, interlaced);
+ }
+ }
+
+ @Override
+ public void onAudioStreamChanged(Session session, int channelCount) {
+ if (DEBUG) {
+ Log.d(TAG, "onAudioStreamChanged(" + channelCount + ")");
+ }
+ if (mListener != null) {
+ mListener.onAudioStreamChanged(mInputId, channelCount);
+ }
+ }
+
+ @Override
+ public void onClosedCaptionStreamChanged(Session session, boolean hasClosedCaption) {
+ if (DEBUG) {
+ Log.d(TAG, "onClosedCaptionStreamChanged(" + hasClosedCaption + ")");
+ }
+ if (mListener != null) {
+ mListener.onClosedCaptionStreamChanged(mInputId, hasClosedCaption);
+ }
+ }
+
+ @Override
+ public void onSessionEvent(TvInputManager.Session session, String eventType,
+ Bundle eventArgs) {
+ if (mListener != null) {
+ mListener.onEvent(mInputId, eventType, eventArgs);
+ }
+ }
+ }
+}
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 157dd49..fbe5340 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -24,6 +24,7 @@
#include <SkBitmap.h>
#include <media/IMediaHTTPService.h>
#include <media/mediametadataretriever.h>
+#include <media/mediascanner.h>
#include <private/media/VideoFrame.h>
#include "jni.h"
@@ -337,17 +338,13 @@
return NULL;
}
- unsigned int len = mediaAlbumArt->mSize;
- char* data = (char*) mediaAlbumArt + sizeof(MediaAlbumArt);
- jbyteArray array = env->NewByteArray(len);
+ jbyteArray array = env->NewByteArray(mediaAlbumArt->size());
if (!array) { // OutOfMemoryError exception has already been thrown.
ALOGE("getEmbeddedPicture: OutOfMemoryError is thrown.");
} else {
- jbyte* bytes = env->GetByteArrayElements(array, NULL);
- if (bytes != NULL) {
- memcpy(bytes, data, len);
- env->ReleaseByteArrayElements(array, bytes, 0);
- }
+ const jbyte* data =
+ reinterpret_cast<const jbyte*>(mediaAlbumArt->data());
+ env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data);
}
// No need to delete mediaAlbumArt here
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index d21b442..321c2e3 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -21,6 +21,7 @@
#include <utils/threads.h>
#include <media/mediascanner.h>
#include <media/stagefright/StagefrightMediaScanner.h>
+#include <private/media/VideoFrame.h>
#include "jni.h"
#include "JNIHelp.h"
@@ -347,21 +348,20 @@
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- char* data = mp->extractAlbumArt(fd);
- if (!data) {
+ MediaAlbumArt* mediaAlbumArt = mp->extractAlbumArt(fd);
+ if (mediaAlbumArt == NULL) {
return NULL;
}
- jsize len = *((uint32_t*)data);
- jbyteArray array = env->NewByteArray(len);
+ jbyteArray array = env->NewByteArray(mediaAlbumArt->size());
if (array != NULL) {
- jbyte* bytes = env->GetByteArrayElements(array, NULL);
- memcpy(bytes, data + 4, len);
- env->ReleaseByteArrayElements(array, bytes, 0);
+ const jbyte* data =
+ reinterpret_cast<const jbyte*>(mediaAlbumArt->data());
+ env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data);
}
done:
- free(data);
+ free(mediaAlbumArt);
// if NewByteArray() returned NULL, an out-of-memory
// exception will have been raised. I just want to
// return null in that case.
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java
new file mode 100644
index 0000000..d90a4bc
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.mediaframeworktest.unit;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Range;
+import android.util.Rational;
+
+/**
+ * <pre>
+ * adb shell am instrument \
+ * -e class 'com.android.mediaframeworktest.unit.RangeTest' \
+ * -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
+ * </pre>
+ */
+public class RangeTest extends junit.framework.TestCase {
+
+ @SmallTest
+ public void testConstructor() {
+ // Trivial, same range
+ Range<Integer> intRange = new Range<Integer>(1, 1);
+
+ assertLower(intRange, 1);
+ assertUpper(intRange, 1);
+
+ // Different values in range
+ Range<Integer> intRange2 = new Range<Integer>(100, 200);
+ assertLower(intRange2, 100);
+ assertUpper(intRange2, 200);
+
+ Range<Float> floatRange = new Range<Float>(Float.NEGATIVE_INFINITY,
+ Float.POSITIVE_INFINITY);
+ assertLower(floatRange, Float.NEGATIVE_INFINITY);
+ assertUpper(floatRange, Float.POSITIVE_INFINITY);
+ }
+
+ @SmallTest
+ public void testIllegalValues() {
+ // Test NPEs
+ try {
+ new Range<Integer>(null, null);
+ fail("Expected exception to be thrown for (null, null)");
+ } catch (NullPointerException e) {
+ // OK: both args are null
+ }
+
+ try {
+ new Range<Integer>(null, 0);
+ fail("Expected exception to be thrown for (null, 0)");
+ } catch (NullPointerException e) {
+ // OK: left arg is null
+ }
+
+ try {
+ new Range<Integer>(0, null);
+ fail("Expected exception to be thrown for (0, null)");
+ } catch (NullPointerException e) {
+ // OK: right arg is null
+ }
+
+ // Test IAEs
+
+ try {
+ new Range<Integer>(50, -50);
+ fail("Expected exception to be thrown for (50, -50)");
+ } catch (IllegalArgumentException e) {
+ // OK: 50 > -50 so it fails
+ }
+
+ try {
+ new Range<Float>(0.0f, Float.NEGATIVE_INFINITY);
+ fail("Expected exception to be thrown for (0.0f, -Infinity)");
+ } catch (IllegalArgumentException e) {
+ // OK: 0.0f is > NEGATIVE_INFINITY, so it fails
+ }
+ }
+
+ @SmallTest
+ public void testEquals() {
+ Range<Float> oneHalf = Range.create(1.0f, 2.0f);
+ Range<Float> oneHalf2 = new Range<Float>(1.0f, 2.0f);
+ assertEquals(oneHalf, oneHalf2);
+ assertHashCodeEquals(oneHalf, oneHalf2);
+
+ Range<Float> twoThirds = new Range<Float>(2.0f, 3.0f);
+ Range<Float> twoThirds2 = Range.create(2.0f, 3.0f);
+ assertEquals(twoThirds, twoThirds2);
+ assertHashCodeEquals(twoThirds, twoThirds2);
+
+ Range<Rational> negativeOneTenthPositiveOneTenth =
+ new Range<Rational>(new Rational(-1, 10), new Rational(1, 10));
+ Range<Rational> negativeOneTenthPositiveOneTenth2 =
+ Range.create(new Rational(-1, 10), new Rational(1, 10));
+ assertEquals(negativeOneTenthPositiveOneTenth, negativeOneTenthPositiveOneTenth2);
+ assertHashCodeEquals(negativeOneTenthPositiveOneTenth, negativeOneTenthPositiveOneTenth2);
+ }
+
+ @SmallTest
+ public void testInRange() {
+ Range<Integer> hundredOneTwo = Range.create(100, 200);
+
+ assertInRange(hundredOneTwo, 100);
+ assertInRange(hundredOneTwo, 200);
+ assertInRange(hundredOneTwo, 150);
+ assertOutOfRange(hundredOneTwo, 99);
+ assertOutOfRange(hundredOneTwo, 201);
+ assertOutOfRange(hundredOneTwo, 100000);
+
+ Range<Float> infinities = Range.create(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
+
+ assertInRange(infinities, Float.NEGATIVE_INFINITY);
+ assertInRange(infinities, Float.POSITIVE_INFINITY);
+ assertInRange(infinities, 0.0f);
+ assertOutOfRange(infinities, Float.NaN);
+
+ Range<Rational> negativeOneTenthPositiveOneTenth =
+ new Range<Rational>(new Rational(-1, 10), new Rational(1, 10));
+ assertInRange(negativeOneTenthPositiveOneTenth, new Rational(-1, 10));
+ assertInRange(negativeOneTenthPositiveOneTenth, new Rational(1, 10));
+ assertInRange(negativeOneTenthPositiveOneTenth, Rational.ZERO);
+ assertOutOfRange(negativeOneTenthPositiveOneTenth, new Rational(-100, 1));
+ assertOutOfRange(negativeOneTenthPositiveOneTenth, new Rational(100, 1));
+ }
+
+ private static <T extends Comparable<? super T>> void assertInRange(Range<T> object, T needle) {
+ assertAction("in-range", object, needle, true, object.inRange(needle));
+ }
+
+ private static <T extends Comparable<? super T>> void assertOutOfRange(Range<T> object,
+ T needle) {
+ assertAction("out-of-range", object, needle, false, object.inRange(needle));
+ }
+
+ private static <T extends Comparable<? super T>> void assertUpper(Range<T> object, T expected) {
+ assertAction("upper", object, expected, object.getUpper());
+ }
+
+ private static <T extends Comparable<? super T>> void assertLower(Range<T> object, T expected) {
+ assertAction("lower", object, expected, object.getLower());
+ }
+
+ private static <T, T2> void assertAction(String action, T object, T2 expected,
+ T2 actual) {
+ assertEquals("Expected " + object + " " + action + " to be ",
+ expected, actual);
+ }
+
+ private static <T, T2> void assertAction(String action, T object, T2 needle, boolean expected,
+ boolean actual) {
+ String expectedMessage = expected ? action : ("not " + action);
+ assertEquals("Expected " + needle + " to be " + expectedMessage + " of " + object,
+ expected, actual);
+ }
+
+ private static <T extends Comparable<? super T>> void assertHashCodeEquals(
+ Range<T> left, Range<T> right) {
+ assertEquals("Left hash code for " + left +
+ " expected to be equal to right hash code for " + right,
+ left.hashCode(), right.hashCode());
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
index 18c0d3e..1bb7db9 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
@@ -19,6 +19,17 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Rational;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+
+import static android.util.Rational.*;
+
/**
* <pre>
* adb shell am instrument \
@@ -27,6 +38,22 @@
* </pre>
*/
public class RationalTest extends junit.framework.TestCase {
+
+ /** (1,1) */
+ private static final Rational UNIT = new Rational(1, 1);
+
+ /**
+ * Test @hide greatest common divisior functionality that cannot be tested in CTS.
+ */
+ @SmallTest
+ public void testGcd() {
+ assertEquals(1, Rational.gcd(1, 2));
+ assertEquals(1, Rational.gcd(2, 3));
+ assertEquals(78, Rational.gcd(5*78, 7*78));
+ assertEquals(1, Rational.gcd(-1, 2));
+ assertEquals(1, Rational.gcd(-2, 3));
+ }
+
@SmallTest
public void testConstructor() {
@@ -52,12 +79,12 @@
// Infinity.
r = new Rational(1, 0);
- assertEquals(0, r.getNumerator());
+ assertEquals(1, r.getNumerator());
assertEquals(0, r.getDenominator());
// Negative infinity.
r = new Rational(-1, 0);
- assertEquals(0, r.getNumerator());
+ assertEquals(-1, r.getNumerator());
assertEquals(0, r.getDenominator());
// NaN.
@@ -67,24 +94,6 @@
}
@SmallTest
- public void testGcd() {
- Rational r = new Rational(1, 2);
- assertEquals(1, r.gcd());
-
- Rational twoThirds = new Rational(2, 3);
- assertEquals(1, twoThirds.gcd());
-
- Rational moreComplicated2 = new Rational(5*78, 7*78);
- assertEquals(78, moreComplicated2.gcd());
-
- Rational oneHalf = new Rational(-1, 2);
- assertEquals(1, oneHalf.gcd());
-
- twoThirds = new Rational(-2, 3);
- assertEquals(1, twoThirds.gcd());
- }
-
- @SmallTest
public void testEquals() {
Rational r = new Rational(1, 2);
assertEquals(1, r.getNumerator());
@@ -118,7 +127,13 @@
assertEquals(moreComplicated, moreComplicated2);
assertEquals(moreComplicated2, moreComplicated);
- Rational nan = new Rational(0, 0);
+ // Zero is always equal to itself
+ Rational zero2 = new Rational(0, 100);
+ assertEquals(ZERO, zero2);
+ assertEquals(zero2, ZERO);
+
+ // NaN is always equal to itself
+ Rational nan = NaN;
Rational nan2 = new Rational(0, 0);
assertTrue(nan.equals(nan));
assertTrue(nan.equals(nan2));
@@ -127,9 +142,9 @@
assertFalse(r.equals(nan));
// Infinities of the same sign are equal.
- Rational posInf = new Rational(1, 0);
+ Rational posInf = POSITIVE_INFINITY;
Rational posInf2 = new Rational(2, 0);
- Rational negInf = new Rational(-1, 0);
+ Rational negInf = NEGATIVE_INFINITY;
Rational negInf2 = new Rational(-2, 0);
assertEquals(posInf, posInf);
assertEquals(negInf, negInf);
@@ -148,4 +163,349 @@
assertFalse(nan.equals(posInf));
assertFalse(nan.equals(negInf));
}
+
+ @SmallTest
+ public void testReduction() {
+ Rational moreComplicated = new Rational(5 * 78, 7 * 78);
+ assertEquals(new Rational(5, 7), moreComplicated);
+ assertEquals(5, moreComplicated.getNumerator());
+ assertEquals(7, moreComplicated.getDenominator());
+
+ Rational posInf = new Rational(5, 0);
+ assertEquals(1, posInf.getNumerator());
+ assertEquals(0, posInf.getDenominator());
+ assertEquals(POSITIVE_INFINITY, posInf);
+
+ Rational negInf = new Rational(-100, 0);
+ assertEquals(-1, negInf.getNumerator());
+ assertEquals(0, negInf.getDenominator());
+ assertEquals(NEGATIVE_INFINITY, negInf);
+
+ Rational zero = new Rational(0, -100);
+ assertEquals(0, zero.getNumerator());
+ assertEquals(1, zero.getDenominator());
+ assertEquals(ZERO, zero);
+
+ Rational flipSigns = new Rational(1, -1);
+ assertEquals(-1, flipSigns.getNumerator());
+ assertEquals(1, flipSigns.getDenominator());
+
+ Rational flipAndReduce = new Rational(100, -200);
+ assertEquals(-1, flipAndReduce.getNumerator());
+ assertEquals(2, flipAndReduce.getDenominator());
+ }
+
+ @SmallTest
+ public void testCompareTo() {
+ // unit is equal to itself
+ assertCompareEquals(UNIT, new Rational(1, 1));
+
+ // NaN is greater than anything but NaN
+ assertCompareEquals(NaN, new Rational(0, 0));
+ assertGreaterThan(NaN, UNIT);
+ assertGreaterThan(NaN, POSITIVE_INFINITY);
+ assertGreaterThan(NaN, NEGATIVE_INFINITY);
+ assertGreaterThan(NaN, ZERO);
+
+ // Positive infinity is greater than any other non-NaN
+ assertCompareEquals(POSITIVE_INFINITY, new Rational(1, 0));
+ assertGreaterThan(POSITIVE_INFINITY, UNIT);
+ assertGreaterThan(POSITIVE_INFINITY, NEGATIVE_INFINITY);
+ assertGreaterThan(POSITIVE_INFINITY, ZERO);
+
+ // Negative infinity is smaller than any other non-NaN
+ assertCompareEquals(NEGATIVE_INFINITY, new Rational(-1, 0));
+ assertLessThan(NEGATIVE_INFINITY, UNIT);
+ assertLessThan(NEGATIVE_INFINITY, POSITIVE_INFINITY);
+ assertLessThan(NEGATIVE_INFINITY, ZERO);
+
+ // A finite number with the same denominator is trivially comparable
+ assertGreaterThan(new Rational(3, 100), new Rational(1, 100));
+ assertGreaterThan(new Rational(3, 100), ZERO);
+
+ // Compare finite numbers with different divisors
+ assertGreaterThan(new Rational(5, 25), new Rational(1, 10));
+ assertGreaterThan(new Rational(5, 25), ZERO);
+
+ // Compare finite numbers with different signs
+ assertGreaterThan(new Rational(5, 25), new Rational(-1, 10));
+ assertLessThan(new Rational(-5, 25), ZERO);
+ }
+
+ @SmallTest
+ public void testConvenienceMethods() {
+ // isFinite
+ assertFinite(ZERO, true);
+ assertFinite(NaN, false);
+ assertFinite(NEGATIVE_INFINITY, false);
+ assertFinite(POSITIVE_INFINITY, false);
+ assertFinite(UNIT, true);
+
+ // isInfinite
+ assertInfinite(ZERO, false);
+ assertInfinite(NaN, false);
+ assertInfinite(NEGATIVE_INFINITY, true);
+ assertInfinite(POSITIVE_INFINITY, true);
+ assertInfinite(UNIT, false);
+
+ // isNaN
+ assertNaN(ZERO, false);
+ assertNaN(NaN, true);
+ assertNaN(NEGATIVE_INFINITY, false);
+ assertNaN(POSITIVE_INFINITY, false);
+ assertNaN(UNIT, false);
+
+ // isZero
+ assertZero(ZERO, true);
+ assertZero(NaN, false);
+ assertZero(NEGATIVE_INFINITY, false);
+ assertZero(POSITIVE_INFINITY, false);
+ assertZero(UNIT, false);
+ }
+
+ @SmallTest
+ public void testValueConversions() {
+ // Unit, simple case
+ assertValueEquals(UNIT, 1.0f);
+ assertValueEquals(UNIT, 1.0);
+ assertValueEquals(UNIT, 1L);
+ assertValueEquals(UNIT, 1);
+ assertValueEquals(UNIT, (short)1);
+
+ // Zero, simple case
+ assertValueEquals(ZERO, 0.0f);
+ assertValueEquals(ZERO, 0.0);
+ assertValueEquals(ZERO, 0L);
+ assertValueEquals(ZERO, 0);
+ assertValueEquals(ZERO, (short)0);
+
+ // NaN is 0 for integers, not-a-number for floating point
+ assertValueEquals(NaN, Float.NaN);
+ assertValueEquals(NaN, Double.NaN);
+ assertValueEquals(NaN, 0L);
+ assertValueEquals(NaN, 0);
+ assertValueEquals(NaN, (short)0);
+
+ // Positive infinity, saturates upwards for integers
+ assertValueEquals(POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
+ assertValueEquals(POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+ assertValueEquals(POSITIVE_INFINITY, Long.MAX_VALUE);
+ assertValueEquals(POSITIVE_INFINITY, Integer.MAX_VALUE);
+ assertValueEquals(POSITIVE_INFINITY, (short)-1);
+
+ // Negative infinity, saturates downwards for integers
+ assertValueEquals(NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+ assertValueEquals(NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+ assertValueEquals(NEGATIVE_INFINITY, Long.MIN_VALUE);
+ assertValueEquals(NEGATIVE_INFINITY, Integer.MIN_VALUE);
+ assertValueEquals(NEGATIVE_INFINITY, (short)0);
+
+ // Normal finite values, round down for integers
+ final Rational oneQuarter = new Rational(1, 4);
+ assertValueEquals(oneQuarter, 1.0f / 4.0f);
+ assertValueEquals(oneQuarter, 1.0 / 4.0);
+ assertValueEquals(oneQuarter, 0L);
+ assertValueEquals(oneQuarter, 0);
+ assertValueEquals(oneQuarter, (short)0);
+
+ final Rational nineFifths = new Rational(9, 5);
+ assertValueEquals(nineFifths, 9.0f / 5.0f);
+ assertValueEquals(nineFifths, 9.0 / 5.0);
+ assertValueEquals(nineFifths, 1L);
+ assertValueEquals(nineFifths, 1);
+ assertValueEquals(nineFifths, (short)1);
+
+ final Rational negativeHundred = new Rational(-1000, 10);
+ assertValueEquals(negativeHundred, -100.f / 1.f);
+ assertValueEquals(negativeHundred, -100.0 / 1.0);
+ assertValueEquals(negativeHundred, -100L);
+ assertValueEquals(negativeHundred, -100);
+ assertValueEquals(negativeHundred, (short)-100);
+
+ // Short truncates if the result is too large
+ assertValueEquals(new Rational(Integer.MAX_VALUE, 1), (short)Integer.MAX_VALUE);
+ assertValueEquals(new Rational(0x00FFFFFF, 1), (short)0x00FFFFFF);
+ assertValueEquals(new Rational(0x00FF00FF, 1), (short)0x00FF00FF);
+ }
+
+ @SmallTest
+ public void testSerialize() throws ClassNotFoundException, IOException {
+ /*
+ * Check correct [de]serialization
+ */
+ assertEqualsAfterSerializing(ZERO);
+ assertEqualsAfterSerializing(NaN);
+ assertEqualsAfterSerializing(NEGATIVE_INFINITY);
+ assertEqualsAfterSerializing(POSITIVE_INFINITY);
+ assertEqualsAfterSerializing(UNIT);
+ assertEqualsAfterSerializing(new Rational(100, 200));
+ assertEqualsAfterSerializing(new Rational(-100, 200));
+ assertEqualsAfterSerializing(new Rational(5, 1));
+ assertEqualsAfterSerializing(new Rational(Integer.MAX_VALUE, Integer.MIN_VALUE));
+
+ /*
+ * Check bad deserialization fails
+ */
+ try {
+ Rational badZero = createIllegalRational(0, 100); // [0, 100] , should be [0, 1]
+ Rational results = serializeRoundTrip(badZero);
+ fail("Deserializing " + results + " should not have succeeded");
+ } catch (InvalidObjectException e) {
+ // OK
+ }
+
+ try {
+ Rational badPosInfinity = createIllegalRational(100, 0); // [100, 0] , should be [1, 0]
+ Rational results = serializeRoundTrip(badPosInfinity);
+ fail("Deserializing " + results + " should not have succeeded");
+ } catch (InvalidObjectException e) {
+ // OK
+ }
+
+ try {
+ Rational badNegInfinity =
+ createIllegalRational(-100, 0); // [-100, 0] , should be [-1, 0]
+ Rational results = serializeRoundTrip(badNegInfinity);
+ fail("Deserializing " + results + " should not have succeeded");
+ } catch (InvalidObjectException e) {
+ // OK
+ }
+
+ try {
+ Rational badReduced = createIllegalRational(2, 4); // [2,4] , should be [1, 2]
+ Rational results = serializeRoundTrip(badReduced);
+ fail("Deserializing " + results + " should not have succeeded");
+ } catch (InvalidObjectException e) {
+ // OK
+ }
+
+ try {
+ Rational badReducedNeg = createIllegalRational(-2, 4); // [-2, 4] should be [-1, 2]
+ Rational results = serializeRoundTrip(badReducedNeg);
+ fail("Deserializing " + results + " should not have succeeded");
+ } catch (InvalidObjectException e) {
+ // OK
+ }
+ }
+
+ private static void assertValueEquals(Rational object, float expected) {
+ assertEquals("Checking floatValue() for " + object + ";",
+ expected, object.floatValue());
+ }
+
+ private static void assertValueEquals(Rational object, double expected) {
+ assertEquals("Checking doubleValue() for " + object + ";",
+ expected, object.doubleValue());
+ }
+
+ private static void assertValueEquals(Rational object, long expected) {
+ assertEquals("Checking longValue() for " + object + ";",
+ expected, object.longValue());
+ }
+
+ private static void assertValueEquals(Rational object, int expected) {
+ assertEquals("Checking intValue() for " + object + ";",
+ expected, object.intValue());
+ }
+
+ private static void assertValueEquals(Rational object, short expected) {
+ assertEquals("Checking shortValue() for " + object + ";",
+ expected, object.shortValue());
+ }
+
+ private static void assertFinite(Rational object, boolean expected) {
+ assertAction("finite", object, expected, object.isFinite());
+ }
+
+ private static void assertInfinite(Rational object, boolean expected) {
+ assertAction("infinite", object, expected, object.isInfinite());
+ }
+
+ private static void assertNaN(Rational object, boolean expected) {
+ assertAction("NaN", object, expected, object.isNaN());
+ }
+
+ private static void assertZero(Rational object, boolean expected) {
+ assertAction("zero", object, expected, object.isZero());
+ }
+
+ private static <T> void assertAction(String action, T object, boolean expected,
+ boolean actual) {
+ String expectedMessage = expected ? action : ("not " + action);
+ assertEquals("Expected " + object + " to be " + expectedMessage,
+ expected, actual);
+ }
+
+ private static <T extends Comparable<? super T>> void assertLessThan(T left, T right) {
+ assertTrue("Expected (LR) left " + left + " to be less than right " + right,
+ left.compareTo(right) < 0);
+ assertTrue("Expected (RL) left " + left + " to be less than right " + right,
+ right.compareTo(left) > 0);
+ }
+
+ private static <T extends Comparable<? super T>> void assertGreaterThan(T left, T right) {
+ assertTrue("Expected (LR) left " + left + " to be greater than right " + right,
+ left.compareTo(right) > 0);
+ assertTrue("Expected (RL) left " + left + " to be greater than right " + right,
+ right.compareTo(left) < 0);
+ }
+
+ private static <T extends Comparable<? super T>> void assertCompareEquals(T left, T right) {
+ assertTrue("Expected (LR) left " + left + " to be compareEquals to right " + right,
+ left.compareTo(right) == 0);
+ assertTrue("Expected (RL) left " + left + " to be compareEquals to right " + right,
+ right.compareTo(left) == 0);
+ }
+
+ private static <T extends Serializable> byte[] serialize(T obj) throws IOException {
+ ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+ try (ObjectOutputStream objectStream = new ObjectOutputStream(byteStream)) {
+ objectStream.writeObject(obj);
+ }
+ return byteStream.toByteArray();
+ }
+
+ private static <T extends Serializable> T deserialize(byte[] array, Class<T> klass)
+ throws IOException, ClassNotFoundException {
+ ByteArrayInputStream bais = new ByteArrayInputStream(array);
+ ObjectInputStream ois = new ObjectInputStream(bais);
+ Object obj = ois.readObject();
+ return klass.cast(obj);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Serializable> T serializeRoundTrip(T obj)
+ throws IOException, ClassNotFoundException {
+ Class<T> klass = (Class<T>) obj.getClass();
+ byte[] arr = serialize(obj);
+ T serialized = deserialize(arr, klass);
+ return serialized;
+ }
+
+ private static <T extends Serializable> void assertEqualsAfterSerializing(T obj)
+ throws ClassNotFoundException, IOException {
+ T serialized = serializeRoundTrip(obj);
+ assertEquals("Expected values to be equal after serialization round-trip", obj, serialized);
+ }
+
+ private static Rational createIllegalRational(int numerator, int denominator) {
+ Rational r = new Rational(numerator, denominator);
+ mutateField(r, "mNumerator", numerator);
+ mutateField(r, "mDenominator", denominator);
+ return r;
+ }
+
+ private static <T> void mutateField(T object, String name, int value) {
+ try {
+ Field f = object.getClass().getDeclaredField(name);
+ f.setAccessible(true);
+ f.set(object, value);
+ } catch (NoSuchFieldException e) {
+ throw new AssertionError(e);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (IllegalArgumentException e) {
+ throw new AssertionError(e);
+ }
+ }
}
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
index 3dd988e..a4d292a 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
@@ -104,7 +104,9 @@
int height;
meta->findInt32(kKeyWidth, &width);
meta->findInt32(kKeyHeight, &height);
- configBitmapSize(bm, getPrefConfig(k32Bit_SrcDepth, false), width, height);
+ configBitmapSize(
+ bm, SkColorTypeToBitmapConfig(getPrefColorType(k32Bit_SrcDepth, false)),
+ width, height);
// mode == DecodeBounds
if (mode == SkImageDecoder::kDecodeBounds_Mode) {
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 3f37ed1..b09bc2e 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -171,3 +171,8 @@
{
return static_cast<Sensor const*>(sensor)->getStringType().string();
}
+
+int ASensor_getReportingMode(ASensor const* sensor)
+{
+ return static_cast<Sensor const*>(sensor)->getReportingMode();
+}
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index b02aefc..ec87c6e 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -88,7 +88,7 @@
private IMediaContainerService.Stub mBinder = new IMediaContainerService.Stub() {
/**
* Creates a new container and copies resource there.
- * @param paackageURI the uri of resource to be copied. Can be either
+ * @param packageURI the uri of resource to be copied. Can be either
* a content uri or a file uri
* @param cid the id of the secure container that should
* be used for creating a secure container into which the resource
@@ -342,9 +342,22 @@
// The .apk file
String codePath = packageURI.getPath();
File codeFile = new File(codePath);
- String[] abiList = (abiOverride != null) ? new String[] { abiOverride }
- : Build.SUPPORTED_ABIS;
NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codePath);
+ String[] abiList = Build.SUPPORTED_ABIS;
+ if (abiOverride != null) {
+ abiList = new String[] { abiOverride };
+ } else {
+ try {
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ }
+ } catch (IOException ioe) {
+ Slog.w(TAG, "Problem determining ABI for: " + codeFile.getPath());
+ return null;
+ }
+ }
+
final int abi = NativeLibraryHelper.findSupportedAbi(handle, abiList);
// Calculate size of container needed to hold base APK.
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 159ee66..3861cc1 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -32,7 +32,7 @@
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
- <action android:name="android.intent.action.PICK_DIRECTORY" />
+ <action android:name="android.intent.action.OPEN_DOCUMENT_TREE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index aa118ed..ae04e32 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="2783841764617238354">"Asiakirjat"</string>
- <string name="title_open" msgid="4353228937663917801">"Avoinna alkaen"</string>
+ <string name="title_open" msgid="4353228937663917801">"Avaa sijainnista"</string>
<string name="title_save" msgid="2433679664882857999">"Tallenna kohteeseen"</string>
<string name="menu_create_dir" msgid="5947289605844398389">"Luo kansio"</string>
<string name="menu_grid" msgid="6878021334497835259">"Ruudukkonäkymä"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 070b130..b85b518 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="2783841764617238354">"Documents"</string>
+ <string name="app_label" msgid="2783841764617238354">"Docs"</string>
<string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string>
<string name="title_save" msgid="2433679664882857999">"Enregistrer sous"</string>
<string name="menu_create_dir" msgid="5947289605844398389">"Créer un dossier"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index e8944ec..8c9030d 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,14 +20,14 @@
<string name="title_open" msgid="4353228937663917801">"បើកពី"</string>
<string name="title_save" msgid="2433679664882857999">"រក្សាទុកទៅ"</string>
<string name="menu_create_dir" msgid="5947289605844398389">"បង្កើតថត"</string>
- <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string>
+ <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាពក្រឡា"</string>
<string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាពបញ្ជី"</string>
<string name="menu_sort" msgid="7677740407158414452">"តម្រៀបតាម"</string>
<string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string>
<string name="menu_settings" msgid="6008033148948428823">"ការកំណត់"</string>
<string name="menu_open" msgid="432922957274920903">"បើក"</string>
<string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string>
- <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
+ <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
<string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
<string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
<string name="mode_selected_count" msgid="459111894725594625">"បានជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string>
@@ -48,7 +48,7 @@
<string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញឧបករណ៍កម្រិតខ្ពស់"</string>
<string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញទំហំឯកសារ"</string>
<string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញទំហំឧបករណ៍"</string>
- <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
+ <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
<string name="toast_no_application" msgid="1339885974067891667">"មិនអាចបើកឯកសារ"</string>
<string name="toast_failed_delete" msgid="2180678019407244069">"មិនអាចលុបឯកសារមួយចំនួន"</string>
<string name="share_via" msgid="8966594246261344259">"ចែករំលែកតាម"</string>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 9f76991..d0b6a1d 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -24,7 +24,7 @@
import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_PICK_DIRECTORY;
+import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN_TREE;
import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
@@ -203,7 +203,7 @@
final String mimeType = getIntent().getType();
final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
SaveFragment.show(getFragmentManager(), mimeType, title);
- } else if (mState.action == ACTION_PICK_DIRECTORY) {
+ } else if (mState.action == ACTION_OPEN_TREE) {
PickFragment.show(getFragmentManager());
}
@@ -213,7 +213,7 @@
moreApps.setPackage(null);
RootsFragment.show(getFragmentManager(), moreApps);
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE
- || mState.action == ACTION_PICK_DIRECTORY) {
+ || mState.action == ACTION_OPEN_TREE) {
RootsFragment.show(getFragmentManager(), null);
}
@@ -240,8 +240,8 @@
mState.action = ACTION_CREATE;
} else if (Intent.ACTION_GET_CONTENT.equals(action)) {
mState.action = ACTION_GET_CONTENT;
- } else if (Intent.ACTION_PICK_DIRECTORY.equals(action)) {
- mState.action = ACTION_PICK_DIRECTORY;
+ } else if (Intent.ACTION_OPEN_DOCUMENT_TREE.equals(action)) {
+ mState.action = ACTION_OPEN_TREE;
} else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) {
mState.action = ACTION_MANAGE;
}
@@ -441,7 +441,7 @@
actionBar.setIcon(new ColorDrawable());
if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT
- || mState.action == ACTION_PICK_DIRECTORY) {
+ || mState.action == ACTION_OPEN_TREE) {
actionBar.setTitle(R.string.title_open);
} else if (mState.action == ACTION_CREATE) {
actionBar.setTitle(R.string.title_save);
@@ -583,7 +583,7 @@
sortSize.setVisible(mState.showSize);
final boolean searchVisible;
- if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) {
+ if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
createDir.setVisible(cwd != null && cwd.isCreateSupported());
searchVisible = false;
@@ -828,7 +828,7 @@
if (cwd == null) {
// No directory means recents
- if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) {
+ if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
RecentsCreateFragment.show(fm);
} else {
DirectoryFragment.showRecentsOpen(fm, anim);
@@ -857,7 +857,7 @@
}
}
- if (mState.action == ACTION_PICK_DIRECTORY) {
+ if (mState.action == ACTION_OPEN_TREE) {
final PickFragment pick = PickFragment.get(fm);
if (pick != null) {
final CharSequence displayName = (mState.stack.size() <= 1) ? root.title
@@ -1021,7 +1021,7 @@
}
public void onPickRequested(DocumentInfo pickTarget) {
- final Uri viaUri = DocumentsContract.buildViaUri(pickTarget.authority,
+ final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority,
pickTarget.documentId);
new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor());
}
@@ -1031,7 +1031,7 @@
final ContentValues values = new ContentValues();
final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
- if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) {
+ if (mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
// Remember stack for last create
values.clear();
values.put(RecentColumns.KEY, mState.stack.buildKey());
@@ -1064,7 +1064,7 @@
if (mState.action == ACTION_GET_CONTENT) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- } else if (mState.action == ACTION_PICK_DIRECTORY) {
+ } else if (mState.action == ACTION_OPEN_TREE) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
@@ -1202,7 +1202,7 @@
public static final int ACTION_OPEN = 1;
public static final int ACTION_CREATE = 2;
public static final int ACTION_GET_CONTENT = 3;
- public static final int ACTION_PICK_DIRECTORY = 4;
+ public static final int ACTION_OPEN_TREE = 4;
public static final int ACTION_MANAGE = 5;
public static final int MODE_UNKNOWN = 0;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index a9e488a1..5112c92 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -77,13 +77,15 @@
public void setPickTarget(DocumentInfo pickTarget, CharSequence displayName) {
mPickTarget = pickTarget;
- if (mPickTarget != null) {
- mContainer.setVisibility(View.VISIBLE);
- final Locale locale = getResources().getConfiguration().locale;
- final String raw = getString(R.string.menu_select).toUpperCase(locale);
- mPick.setText(TextUtils.expandTemplate(raw, displayName));
- } else {
- mContainer.setVisibility(View.GONE);
+ if (mContainer != null) {
+ if (mPickTarget != null) {
+ mContainer.setVisibility(View.VISIBLE);
+ final Locale locale = getResources().getConfiguration().locale;
+ final String raw = getString(R.string.menu_select).toUpperCase(locale);
+ mPick.setText(TextUtils.expandTemplate(raw, displayName));
+ } else {
+ mContainer.setVisibility(View.GONE);
+ }
}
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index 933dbe0..caa7581 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -105,7 +105,7 @@
mRecentsRoot.rootId = null;
mRecentsRoot.icon = R.drawable.ic_root_recent;
mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE
- | Root.FLAG_SUPPORTS_DIR_SELECTION;
+ | Root.FLAG_SUPPORTS_IS_CHILD;
mRecentsRoot.title = mContext.getString(R.string.root_recent);
mRecentsRoot.availableBytes = -1;
@@ -350,7 +350,7 @@
final List<RootInfo> matching = Lists.newArrayList();
for (RootInfo root : roots) {
final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0;
- final boolean supportsDir = (root.flags & Root.FLAG_SUPPORTS_DIR_SELECTION) != 0;
+ final boolean supportsIsChild = (root.flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0;
final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0;
final boolean localOnly = (root.flags & Root.FLAG_LOCAL_ONLY) != 0;
final boolean empty = (root.flags & Root.FLAG_EMPTY) != 0;
@@ -358,7 +358,7 @@
// Exclude read-only devices when creating
if (state.action == State.ACTION_CREATE && !supportsCreate) continue;
// Exclude roots that don't support directory picking
- if (state.action == State.ACTION_PICK_DIRECTORY && !supportsDir) continue;
+ if (state.action == State.ACTION_OPEN_TREE && !supportsIsChild) continue;
// Exclude advanced devices when not requested
if (!state.showAdvanced && advanced) continue;
// Exclude non-local devices when local only
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index d388ab7..9473eb9 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -145,7 +145,7 @@
final RootInfo root = new RootInfo();
root.rootId = rootId;
root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
- | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_DIR_SELECTION;
+ | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;
if (ROOT_ID_PRIMARY_EMULATED.equals(rootId)) {
root.title = getContext().getString(R.string.root_internal_storage);
} else {
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 112e371a..2917faa 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -48,5 +48,18 @@
android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
<include layout="@layout/keyguard_status_area" />
+ <TextView
+ android:id="@+id/owner_info"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/date_owner_info_margin"
+ android:layout_gravity="center_horizontal"
+ android:textColor="#99ffffff"
+ android:textSize="@dimen/widget_label_font_size"
+ android:ellipsize="marquee"
+ android:singleLine="true" />
+
</LinearLayout>
</com.android.keyguard.KeyguardStatusView>
diff --git a/packages/Keyguard/res/values-af/strings.xml b/packages/Keyguard/res/values-af/strings.xml
index a046dc5..5811d14 100644
--- a/packages/Keyguard/res/values-af/strings.xml
+++ b/packages/Keyguard/res/values-af/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Om te ontsluit, druk Kieslys dan 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maksimum gesigontsluit-pogings oorskry"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Gelaai"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Laai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Laai tans"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Koppel jou herlaaier."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Druk kieslys om te ontsluit."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Netwerk gesluit"</string>
diff --git a/packages/Keyguard/res/values-am/strings.xml b/packages/Keyguard/res/values-am/strings.xml
index 0bb7993..a630dee 100644
--- a/packages/Keyguard/res/values-am/strings.xml
+++ b/packages/Keyguard/res/values-am/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"ለመክፈት፣ምናሌ ተጫን ከዛ 0"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"የመጨረሻውን የገጽ ክፈት ሙከራዎችን አልፏል"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"ባትሪ ሞልቷል"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"ባትሪ በመሙላት ላይ፣ <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"ኃይል በመሙላት ላይ"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"የኃይል መሙያዎን ይሰኩ።"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"ለመክፈት ምናሌውን ይጫኑ።"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"አውታረ መረብ ተቆልፏል"</string>
diff --git a/packages/Keyguard/res/values-ar/strings.xml b/packages/Keyguard/res/values-ar/strings.xml
index eac3216..48a5fca 100644
--- a/packages/Keyguard/res/values-ar/strings.xml
+++ b/packages/Keyguard/res/values-ar/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"لإلغاء التأمين، اضغط على \"القائمة\" ثم على 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"تم تجاوز الحد الأقصى لعدد محاولات تأمين الجهاز بالوجه"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"تم الشحن"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"جارٍ الشحن، <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"جارٍ الشحن"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"توصيل جهاز الشحن."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"اضغط على \"القائمة\" لإلغاء القفل."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"الشبكة مؤمّنة"</string>
diff --git a/packages/Keyguard/res/values-bg/strings.xml b/packages/Keyguard/res/values-bg/strings.xml
index ad4285a..e8d5533 100644
--- a/packages/Keyguard/res/values-bg/strings.xml
+++ b/packages/Keyguard/res/values-bg/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"За да отключите, натиснете „Меню“ и после 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Максималният брой опити за отключване с лице е надвишен"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Заредена"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Зарежда се, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Зарежда се"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Свържете зарядното си устройство."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Натиснете иконата за меню, за да отключите."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Мрежата е заключена"</string>
diff --git a/packages/Keyguard/res/values-ca/strings.xml b/packages/Keyguard/res/values-ca/strings.xml
index 7e40709..d23d487 100644
--- a/packages/Keyguard/res/values-ca/strings.xml
+++ b/packages/Keyguard/res/values-ca/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Per desbloquejar-lo, premeu Menú i després 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"S\'ha superat el nombre màxim d\'intents de desbloqueig facial"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Carregada"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"S\'està carregant, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Carregant"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Connecta el carregador."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Prem Menú per desbloquejar."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Xarxa bloquejada"</string>
diff --git a/packages/Keyguard/res/values-cs/strings.xml b/packages/Keyguard/res/values-cs/strings.xml
index 53cc707..5df0508 100644
--- a/packages/Keyguard/res/values-cs/strings.xml
+++ b/packages/Keyguard/res/values-cs/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Chcete-li telefon odemknout, stiskněte Menu a poté 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Překročili jste maximální povolený počet pokusů o odemknutí obličejem."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Nabito"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Nabíjení, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Nabíjení"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Připojte dobíjecí zařízení."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Telefon odemknete stisknutím tlačítka Menu."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Síť je blokována"</string>
diff --git a/packages/Keyguard/res/values-da/strings.xml b/packages/Keyguard/res/values-da/strings.xml
index cf1aad9..85ebf4b 100644
--- a/packages/Keyguard/res/values-da/strings.xml
+++ b/packages/Keyguard/res/values-da/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Tryk på Menu og dernæst på 0 for at låse op."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Det maksimale antal forsøg på at bruge Ansigtslås er overskredet"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Opladet"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Oplader, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Oplader"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Tilslut din oplader."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Tryk på Menu for at låse op."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Netværket er låst"</string>
diff --git a/packages/Keyguard/res/values-de/strings.xml b/packages/Keyguard/res/values-de/strings.xml
index 14df237..bf88124 100644
--- a/packages/Keyguard/res/values-de/strings.xml
+++ b/packages/Keyguard/res/values-de/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Drücken Sie zum Entsperren die Menütaste und dann auf \"0\"."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Die maximal zulässige Anzahl an Face Unlock-Versuchen wurde überschritten."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Aufgeladen"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Akku wird aufgeladen (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Wird aufgeladen"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Bitte Ladegerät anschließen"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Zum Entsperren die Menütaste drücken"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Netzwerk gesperrt"</string>
diff --git a/packages/Keyguard/res/values-el/strings.xml b/packages/Keyguard/res/values-el/strings.xml
index 63d8409..e928662 100644
--- a/packages/Keyguard/res/values-el/strings.xml
+++ b/packages/Keyguard/res/values-el/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Για ξεκλείδωμα, πατήστε το πλήκτρο Menu και, στη συνέχεια, το πλήκτρο 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Έγινε υπέρβαση του μέγιστου αριθμού προσπαθειών Face Unlock"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Φορτίστηκε"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Φόρτιση, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Φόρτιση"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Συνδέστε τον φορτιστή."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Πατήστε \"Μενού\" για ξεκλείδωμα."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Το δίκτυο κλειδώθηκε"</string>
diff --git a/packages/Keyguard/res/values-en-rGB/strings.xml b/packages/Keyguard/res/values-en-rGB/strings.xml
index ecc850d..962c80d 100644
--- a/packages/Keyguard/res/values-en-rGB/strings.xml
+++ b/packages/Keyguard/res/values-en-rGB/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Charged"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Charging, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Charging"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Connect your charger."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Press Menu to unlock."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Network locked"</string>
diff --git a/packages/Keyguard/res/values-en-rIN/strings.xml b/packages/Keyguard/res/values-en-rIN/strings.xml
index ecc850d..962c80d 100644
--- a/packages/Keyguard/res/values-en-rIN/strings.xml
+++ b/packages/Keyguard/res/values-en-rIN/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"To unlock, press Menu, then 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximum Face Unlock attempts exceeded"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Charged"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Charging, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Charging"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Connect your charger."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Press Menu to unlock."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Network locked"</string>
diff --git a/packages/Keyguard/res/values-es-rUS/strings.xml b/packages/Keyguard/res/values-es-rUS/strings.xml
index c6d63ba..47288a1 100644
--- a/packages/Keyguard/res/values-es-rUS/strings.xml
+++ b/packages/Keyguard/res/values-es-rUS/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, presiona el menú y luego 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Se superó el máximo de intentos permitido para el desbloqueo facial del dispositivo."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Cargada"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Cargando"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Conecta tu cargador."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Presiona Menú para desbloquear."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Bloqueada para la red"</string>
diff --git a/packages/Keyguard/res/values-es/strings.xml b/packages/Keyguard/res/values-es/strings.xml
index 8a78399..1bf3b0c 100644
--- a/packages/Keyguard/res/values-es/strings.xml
+++ b/packages/Keyguard/res/values-es/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Se ha superado el número máximo de intentos de desbloqueo facial."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Cargada"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Cargando"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Conecta el cargador."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Ve al menú para desbloquear la pantalla."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Bloqueada para la red"</string>
diff --git a/packages/Keyguard/res/values-et-rEE/strings.xml b/packages/Keyguard/res/values-et-rEE/strings.xml
index b837f01..ad43c08 100644
--- a/packages/Keyguard/res/values-et-rEE/strings.xml
+++ b/packages/Keyguard/res/values-et-rEE/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Avamiseks vajutage menüüklahvi, seejärel klahvi 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maksimaalne teenusega Face Unlock avamise katsete arv on ületatud"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Laetud"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Laadimine, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Laadimine"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Ühendage laadija."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Avamiseks vajutage menüüklahvi."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Võrk on suletud"</string>
diff --git a/packages/Keyguard/res/values-fa/strings.xml b/packages/Keyguard/res/values-fa/strings.xml
index 5d1c487..fe9652c 100644
--- a/packages/Keyguard/res/values-fa/strings.xml
+++ b/packages/Keyguard/res/values-fa/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"برای بازگشایی قفل، منو را فشار دهید و سپس 0 را فشار دهید."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"دفعات تلاش برای Face Unlock از حداکثر مجاز بیشتر شد"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"شارژ شد"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"شارژ، <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"شارژ کردن"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"شارژر خود را وصل کنید."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"برای بازگشایی قفل روی منو فشار دهید."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"شبکه قفل شد"</string>
diff --git a/packages/Keyguard/res/values-fi/strings.xml b/packages/Keyguard/res/values-fi/strings.xml
index bdf6677..4a18898 100644
--- a/packages/Keyguard/res/values-fi/strings.xml
+++ b/packages/Keyguard/res/values-fi/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Poista lukitus painamalla Valikko-painiketta ja 0-näppäintä."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Face Unlock -yrityksiä tehty suurin sallittu määrä."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Täynnä"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Ladataan (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Ladataan"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Kytke laturi."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Poista lukitus painamalla Valikko-painiketta."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Verkko lukittu"</string>
diff --git a/packages/Keyguard/res/values-fr-rCA/strings.xml b/packages/Keyguard/res/values-fr-rCA/strings.xml
index e77927c..a3960bf 100644
--- a/packages/Keyguard/res/values-fr-rCA/strings.xml
+++ b/packages/Keyguard/res/values-fr-rCA/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le téléphone, appuyez sur \"Menu\", puis sur 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Nombre maximal autorisé de tentatives Face Unlock atteint."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Chargé"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"En charge (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Charge en cours..."</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Branchez votre chargeur."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Réseau verrouillé"</string>
diff --git a/packages/Keyguard/res/values-fr/strings.xml b/packages/Keyguard/res/values-fr/strings.xml
index 41be1eb..6416e53 100644
--- a/packages/Keyguard/res/values-fr/strings.xml
+++ b/packages/Keyguard/res/values-fr/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Pour déverrouiller le clavier, appuyez sur \"Menu\" puis sur 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Nombre maximal autorisé de tentatives Face Unlock atteint."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Chargé"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"En charge (<xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Batterie en charge…"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Branchez votre chargeur."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Appuyez sur \"Menu\" pour déverrouiller l\'appareil."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Réseau verrouillé"</string>
diff --git a/packages/Keyguard/res/values-hi/strings.xml b/packages/Keyguard/res/values-hi/strings.xml
index c963beb..200b42e 100644
--- a/packages/Keyguard/res/values-hi/strings.xml
+++ b/packages/Keyguard/res/values-hi/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"अनलॉक करने के लिए, मेनू दबाएं और फिर 0 दबाएं."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"फेस अनलॉक के अधिकतम प्रयासों की सीमा पार हो गई"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"चार्ज हो गई"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"चार्ज हो रही है, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"चार्ज हो रहा है"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"अपना चार्जर कनेक्ट करें."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"अनलॉक करने के लिए मेनू दबाएं."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"नेटवर्क लॉक किया गया"</string>
diff --git a/packages/Keyguard/res/values-hr/strings.xml b/packages/Keyguard/res/values-hr/strings.xml
index 6bbdd51..5c6a467 100644
--- a/packages/Keyguard/res/values-hr/strings.xml
+++ b/packages/Keyguard/res/values-hr/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Za otključavanje pritisnite Izbornik pa 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Premašen je maksimalni broj Otključavanja licem"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Napunjeno"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Puni se, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Punjenje"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Priključite punjač."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Pritisnite Izbornik za otključavanje."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Mreža je zaključana"</string>
diff --git a/packages/Keyguard/res/values-hu/strings.xml b/packages/Keyguard/res/values-hu/strings.xml
index 9706874..93b9948 100644
--- a/packages/Keyguard/res/values-hu/strings.xml
+++ b/packages/Keyguard/res/values-hu/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"A feloldáshoz nyomja meg a Menü, majd a 0 gombot."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Elérte az arcalapú feloldási kísérletek maximális számát"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Feltöltve"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Töltés (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Töltés"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Csatlakoztassa a töltőt."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"A feloldáshoz nyomja meg a Menü gombot."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"A hálózat lezárva"</string>
diff --git a/packages/Keyguard/res/values-hy-rAM/strings.xml b/packages/Keyguard/res/values-hy-rAM/strings.xml
index 60c626d..0a4c8e0 100644
--- a/packages/Keyguard/res/values-hy-rAM/strings.xml
+++ b/packages/Keyguard/res/values-hy-rAM/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Ապակողպման համար սեղմեք Ցանկ, ապա 0:"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Առավելագույն Դեմքով ապակողպման փորձերը գերազանցված են"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Լիցքավորված է"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Լիցքավորում, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Լիցքավորում"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Միացրեք ձեր լիցքավորիչը:"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Ապակողպելու համար սեղմեք Ցանկը:"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Ցանցը կողպված է"</string>
diff --git a/packages/Keyguard/res/values-in/strings.xml b/packages/Keyguard/res/values-in/strings.xml
index 9ea5a29..343c320 100644
--- a/packages/Keyguard/res/values-in/strings.xml
+++ b/packages/Keyguard/res/values-in/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka, tekan Menu lalu 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Percobaan Face Unlock melebihi batas maksimum"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Terisi"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Mengisi baterai, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Mengisi daya"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Hubungkan pengisi daya."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Tekan Menu untuk membuka."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Jaringan terkunci"</string>
diff --git a/packages/Keyguard/res/values-it/strings.xml b/packages/Keyguard/res/values-it/strings.xml
index 2a481e6..6c7cbfe 100644
--- a/packages/Keyguard/res/values-it/strings.xml
+++ b/packages/Keyguard/res/values-it/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Per sbloccare, premi Menu, poi 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Numero massimo di tentativi di Sblocco col sorriso superato"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Carico"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"In carica (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"In carica"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Collega il caricabatterie."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Premi Menu per sbloccare."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rete bloccata"</string>
diff --git a/packages/Keyguard/res/values-iw/strings.xml b/packages/Keyguard/res/values-iw/strings.xml
index a6a3192..7f73b5a 100644
--- a/packages/Keyguard/res/values-iw/strings.xml
+++ b/packages/Keyguard/res/values-iw/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"כדי לבטל את הנעילה, לחץ על \'תפריט\' ולאחר מכן על 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"חרגת ממספר הניסיונות המרבי של זיהוי פנים"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"טעון"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"טוען, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"טוען"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"חבר את המטען."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"לחץ על \'תפריט\' כדי לבטל את הנעילה."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"רשת נעולה"</string>
diff --git a/packages/Keyguard/res/values-ja/strings.xml b/packages/Keyguard/res/values-ja/strings.xml
index b683a9d..3b7ea6a 100644
--- a/packages/Keyguard/res/values-ja/strings.xml
+++ b/packages/Keyguard/res/values-ja/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"MENU、0キーでロック解除"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"フェイスアンロックの最大試行回数を超えました"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"充電完了"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"充電中: <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"充電中"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"充電してください。"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"メニューからロックを解除できます。"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"ネットワークがロックされました"</string>
diff --git a/packages/Keyguard/res/values-ka-rGE/strings.xml b/packages/Keyguard/res/values-ka-rGE/strings.xml
index 4414096..36a2d79 100644
--- a/packages/Keyguard/res/values-ka-rGE/strings.xml
+++ b/packages/Keyguard/res/values-ka-rGE/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"განბლოკვისათვის დააჭირეთ მენიუს და შემდეგ 0-ს."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"სახის ამოცნობით განბლოკვის მცდელობამ დაშვებულ რაოდენობას გადააჭარბა"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"დამუხტულია"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"მიმდინარეობს დამუხტვა (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"მიმდინარეობს დატენვა"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"შეაერთეთ დამტენი."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"განბლოკვისთვის დააჭირეთ მენიუს."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"ქსელი ჩაკეტილია"</string>
diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml
index a2e54a7..c26b1b4 100644
--- a/packages/Keyguard/res/values-km-rKH/strings.xml
+++ b/packages/Keyguard/res/values-km-rKH/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"ដើម្បីដោះសោ ចុចម៉ឺនុយ បន្ទាប់មក 0 ។"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"បានលើសការព្យាយាមដោះសោតាមទម្រង់មុខ"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"បានបញ្ចូលពេញ"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"បញ្ចូលថ្ម <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"កំពុងបញ្ចូលថ្ម"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"ភ្ជាប់ឧបករណ៍បញ្ចូលថ្ម។"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"ចុចម៉ឺនុយ ដើម្បីដោះសោ។"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"បណ្ដាញជាប់សោ"</string>
@@ -83,7 +83,7 @@
<string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
<string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
<string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
+ <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះបង់"</string>
<string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
<string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
<string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូររបៀប"</string>
@@ -120,7 +120,7 @@
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាមលំនាំច្រើនពេក"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បីដោះសោ ចូលក្នុងគណនី Google ។"</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះអ្នកប្រើ (អ៊ីម៉ែល)"</string>
- <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+ <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
<string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់មិនត្រឹមត្រូវ។"</string>
<string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេចឈ្មោះអ្នកប្រើ ឬពាក្យសម្ងាត់របស់អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
diff --git a/packages/Keyguard/res/values-ko/strings.xml b/packages/Keyguard/res/values-ko/strings.xml
index cde2b1c..994c532 100644
--- a/packages/Keyguard/res/values-ko/strings.xml
+++ b/packages/Keyguard/res/values-ko/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"잠금해제하려면 메뉴를 누른 다음 0을 누릅니다."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"얼굴 인식 잠금해제 최대 시도 횟수를 초과했습니다."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"충전됨"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"충전 중(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"충전 중"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"충전기를 연결하세요."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"잠금해제하려면 메뉴를 누르세요."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"네트워크 잠김"</string>
diff --git a/packages/Keyguard/res/values-lo-rLA/strings.xml b/packages/Keyguard/res/values-lo-rLA/strings.xml
index 0571768..cff4cd5 100644
--- a/packages/Keyguard/res/values-lo-rLA/strings.xml
+++ b/packages/Keyguard/res/values-lo-rLA/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"ເພື່ອປົດລັອກ, ໃຫ້ກົດເມນູ ແລ້ວກົດ 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"ຄວາມພະຍາຍາມປົດລັອກດ້ວຍໜ້ານັ້ນ ເກີນຈຳນວນທີ່ກຳນົດແລ້ວ"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"ສາກເຕັມແລ້ວ"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"ກຳລັງສາກ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"ກຳລັງສາກໄຟ"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"ເຊື່ອມຕໍ່ອຸປະກອນສາກຂອງທ່ານ."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"ກົດເມນູເພື່ອປົດລັອກ."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"ເຄືອຂ່າຍຖືກລັອກ"</string>
diff --git a/packages/Keyguard/res/values-lt/strings.xml b/packages/Keyguard/res/values-lt/strings.xml
index 0fd6605..e965076 100644
--- a/packages/Keyguard/res/values-lt/strings.xml
+++ b/packages/Keyguard/res/values-lt/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Jei norite atrakinti, paspauskite „Meniu“ ir 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Viršijote maksimalų atrakinimo pagal veidą bandymų skaičių"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Įkrauta"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Įkraunama, <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Kraunama"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Prijunkite įkroviklį."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Jei norite atrakinti, paspauskite „Meniu“."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Tinklas užrakintas"</string>
diff --git a/packages/Keyguard/res/values-lv/strings.xml b/packages/Keyguard/res/values-lv/strings.xml
index 2bcde1d..144fe88 100644
--- a/packages/Keyguard/res/values-lv/strings.xml
+++ b/packages/Keyguard/res/values-lv/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Lai atbloķētu, nospiediet Izvēlne, pēc tam 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Ir pārsniegts maksimālais Autorizācijas pēc sejas mēģinājumu skaits."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Uzlādēts"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Notiek uzlāde (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Notiek uzlāde"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Pievienojiet uzlādes ierīci."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Lai atbloķētu, nospiediet vienumu Izvēlne."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Tīkls ir bloķēts."</string>
diff --git a/packages/Keyguard/res/values-mn-rMN/strings.xml b/packages/Keyguard/res/values-mn-rMN/strings.xml
index 7bb819d..200e104 100644
--- a/packages/Keyguard/res/values-mn-rMN/strings.xml
+++ b/packages/Keyguard/res/values-mn-rMN/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Тайлах бол Цэсийг дараад 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Нүүрээр түгжээ тайлах оролдлогын тоо дээд хэмжээнээс хэтэрсэн"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Цэнэглэгдэв"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Цэнэглэж байна, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Цэнэглэж байна"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Цэнэглэгчээ холбоно уу."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Тайлх бол цэсийг дарна уу."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Сүлжээ түгжигдсэн"</string>
diff --git a/packages/Keyguard/res/values-ms-rMY/strings.xml b/packages/Keyguard/res/values-ms-rMY/strings.xml
index b4c1b46..0592b34 100644
--- a/packages/Keyguard/res/values-ms-rMY/strings.xml
+++ b/packages/Keyguard/res/values-ms-rMY/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Untuk membuka kunci, tekan Menu, kemudian 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Telah melepasi had cubaan Buka Kunci Wajah"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Sudah dicas"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Mengecas, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Mengecas"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Sambungkan pengecas anda."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Tekan Menu untuk membuka kunci."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rangkaian dikunci"</string>
diff --git a/packages/Keyguard/res/values-nb/strings.xml b/packages/Keyguard/res/values-nb/strings.xml
index 801e03d..44b9641 100644
--- a/packages/Keyguard/res/values-nb/strings.xml
+++ b/packages/Keyguard/res/values-nb/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på menyknappen og deretter 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har overskredet grensen for opplåsingsforsøk med Ansiktslås"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Oppladet"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Lader: <xliff:g id="NUMBER">%d</xliff:g> <xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Lader"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Koble til laderen."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Trykk på Meny for å låse opp."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Nettverk låst"</string>
diff --git a/packages/Keyguard/res/values-nl/strings.xml b/packages/Keyguard/res/values-nl/strings.xml
index 195f950..5006f49 100644
--- a/packages/Keyguard/res/values-nl/strings.xml
+++ b/packages/Keyguard/res/values-nl/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Druk op \'Menu\' en vervolgens op 0 om te ontgrendelen."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Maximaal aantal pogingen voor Ontgrendelen via gezichtsherkenning overschreden"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Opgeladen"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Opladen, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Opladen"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Sluit de oplader aan."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Druk op \'Menu\' om te ontgrendelen."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Netwerk vergrendeld"</string>
diff --git a/packages/Keyguard/res/values-pl/strings.xml b/packages/Keyguard/res/values-pl/strings.xml
index 165b2c4..8e22a5a 100644
--- a/packages/Keyguard/res/values-pl/strings.xml
+++ b/packages/Keyguard/res/values-pl/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Aby odblokować, naciśnij Menu, a następnie 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Przekroczono maksymalną liczbę prób rozpoznania twarzy."</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Naładowana"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Ładowanie (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Ładuje się"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Podłącz ładowarkę."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Naciśnij Menu, by odblokować."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Zablokowana sieć"</string>
diff --git a/packages/Keyguard/res/values-pt-rPT/strings.xml b/packages/Keyguard/res/values-pt-rPT/strings.xml
index 332a943..443f4c2 100644
--- a/packages/Keyguard/res/values-pt-rPT/strings.xml
+++ b/packages/Keyguard/res/values-pt-rPT/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, prima Menu e, em seguida, 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Excedido o n.º máximo de tentativas de Desbloqueio Através do Rosto"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Carregado"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"A carregar, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"A carregar"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Ligue o carregador."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Prima Menu para desbloquear."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rede bloqueada"</string>
diff --git a/packages/Keyguard/res/values-pt/strings.xml b/packages/Keyguard/res/values-pt/strings.xml
index a97b1b6..f7d6c1b 100644
--- a/packages/Keyguard/res/values-pt/strings.xml
+++ b/packages/Keyguard/res/values-pt/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, pressione Menu e, em seguida, 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Carregado"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Carregando, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Carregando"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Conecte seu carregador."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Pressione \"Menu\" para desbloquear."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rede bloqueada"</string>
diff --git a/packages/Keyguard/res/values-rm/strings.xml b/packages/Keyguard/res/values-rm/strings.xml
index 4d71f27..9ce72a4 100644
--- a/packages/Keyguard/res/values-rm/strings.xml
+++ b/packages/Keyguard/res/values-rm/strings.xml
@@ -43,7 +43,7 @@
<skip />
<!-- no translation found for keyguard_charged (3272223906073492454) -->
<skip />
- <!-- no translation found for keyguard_plugged_in (8117572000639998388) -->
+ <!-- no translation found for keyguard_plugged_in (9087497435553252863) -->
<skip />
<!-- no translation found for keyguard_low_battery (8143808018719173859) -->
<skip />
diff --git a/packages/Keyguard/res/values-ro/strings.xml b/packages/Keyguard/res/values-ro/strings.xml
index 58bc337..2ab07b4 100644
--- a/packages/Keyguard/res/values-ro/strings.xml
+++ b/packages/Keyguard/res/values-ro/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Pentru a debloca, apăsaţi Meniu, apoi 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"S-a depăşit numărul maxim de încercări pentru Deblocare facială"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Încărcată"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Se încarcă, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Se încarcă"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Conectați încărcătorul."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Apăsați pe Meniu pentru a debloca."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Rețea blocată"</string>
diff --git a/packages/Keyguard/res/values-ru/strings.xml b/packages/Keyguard/res/values-ru/strings.xml
index 866abc0..56e9e54 100644
--- a/packages/Keyguard/res/values-ru/strings.xml
+++ b/packages/Keyguard/res/values-ru/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Для разблокировки нажмите \"Меню\", а затем 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Все попытки войти с помощью Фейсконтроля использованы"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Батарея заряжена"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Идет зарядка (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Зарядка батареи"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Подключите зарядное устройство."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Для разблокировки нажмите \"Меню\"."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Сеть заблокирована"</string>
diff --git a/packages/Keyguard/res/values-sk/strings.xml b/packages/Keyguard/res/values-sk/strings.xml
index 45e4288..de18f14 100644
--- a/packages/Keyguard/res/values-sk/strings.xml
+++ b/packages/Keyguard/res/values-sk/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Ak chcete telefón odomknúť, stlačte Menu a následne 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Prekročili ste maximálny povolený počet pokusov o odomknutie tvárou"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Batéria je nabitá"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Nabíjanie, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Nabíja sa"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Pripojte nabíjačku."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Telefón odomknete stlačením tlačidla Menu."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Sieť je zablokovaná"</string>
diff --git a/packages/Keyguard/res/values-sl/strings.xml b/packages/Keyguard/res/values-sl/strings.xml
index cf72e47..981fb44 100644
--- a/packages/Keyguard/res/values-sl/strings.xml
+++ b/packages/Keyguard/res/values-sl/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Če želite telefon odkleniti, pritisnite meni in nato 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Presegli ste dovoljeno število poskusov odklepanja z obrazom"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Napolnjeno"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Polnjenje, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Polnjenje"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Priključite napajalnik."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Če želite odkleniti, pritisnite meni."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Omrežje je zaklenjeno"</string>
diff --git a/packages/Keyguard/res/values-sr/strings.xml b/packages/Keyguard/res/values-sr/strings.xml
index bd08eae..b944237 100644
--- a/packages/Keyguard/res/values-sr/strings.xml
+++ b/packages/Keyguard/res/values-sr/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Да бисте откључали, притисните „Мени“, а затим 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Премашен је највећи дозвољени број покушаја Откључавања лицем"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Напуњено"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Пуњење, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Пуњење"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Повежите пуњач."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Притисните Мени да бисте откључали."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Мрежа је закључана"</string>
@@ -128,8 +128,8 @@
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8276745642049502550">"Унели сте PIN неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Унели сте лозинку неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. \n\nПокушајте поново за <xliff:g id="NUMBER_1">%d</xliff:g> секунде(и)."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја таблет ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја телефон ће бити враћен на подразумевана фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. Након још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја таблет ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја телефон ће бити ресетован на фабричка подешавања и сви кориснички подаци ће бити изгубљени."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Покушали сте да откључате таблет неисправно <xliff:g id="NUMBER">%d</xliff:g> пута. Таблет ће сада бити враћен на подразумевана фабричка подешавања."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Покушали сте да откључате телефон неисправно <xliff:g id="NUMBER">%d</xliff:g> пута. Телефон ће сада бити враћен на подразумевана фабричка подешавања."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате таблет помоћу налога е-поште.\n\nПокушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
diff --git a/packages/Keyguard/res/values-sv/strings.xml b/packages/Keyguard/res/values-sv/strings.xml
index 1214100..a3c7295 100644
--- a/packages/Keyguard/res/values-sv/strings.xml
+++ b/packages/Keyguard/res/values-sv/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Tryck på Menu och sedan på 0 om du vill låsa upp."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har försökt låsa upp med Ansiktslås för många gånger"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Batteriet har laddats"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Laddar, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Debitering"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Anslut din laddare."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Tryck på Meny om du vill låsa upp."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Nätverk låst"</string>
diff --git a/packages/Keyguard/res/values-sw/strings.xml b/packages/Keyguard/res/values-sw/strings.xml
index fc1ce17..ca2ce47 100644
--- a/packages/Keyguard/res/values-sw/strings.xml
+++ b/packages/Keyguard/res/values-sw/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Ili kufungua, bofya Menyu kisha 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Majaribio ya Juu ya Kufungua Uso yamezidishwa"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Betri imejaa"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Inachaji, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Inachaji"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Unganisha chaja yako."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Bonyeza Menyu ili kufungua."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Mtandao umefungwa"</string>
diff --git a/packages/Keyguard/res/values-sw600dp/dimens.xml b/packages/Keyguard/res/values-sw600dp/dimens.xml
index b954792..e9e9b89 100644
--- a/packages/Keyguard/res/values-sw600dp/dimens.xml
+++ b/packages/Keyguard/res/values-sw600dp/dimens.xml
@@ -70,4 +70,7 @@
<!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
Should be 0 on devices with plenty of room (e.g. tablets) -->
<dimen name="eca_overlap">0dip</dimen>
+
+ <!-- The vertical margin between the date and the owner info. -->
+ <dimen name="date_owner_info_margin">4dp</dimen>
</resources>
diff --git a/packages/Keyguard/res/values-th/strings.xml b/packages/Keyguard/res/values-th/strings.xml
index 34d97c2..368e004 100644
--- a/packages/Keyguard/res/values-th/strings.xml
+++ b/packages/Keyguard/res/values-th/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"หากต้องการปลดล็อก กด เมนู ตามด้วย 0"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"มีความพยายามที่จะใช้ Face Unlock เกินขีดจำกัด"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"ชาร์จแล้ว"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"กำลังชาร์จ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"กำลังชาร์จ"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"เสียบที่ชาร์จของคุณ"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"กด \"เมนู\" เพื่อปลดล็อก"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"เครือข่ายล็อก"</string>
diff --git a/packages/Keyguard/res/values-tl/strings.xml b/packages/Keyguard/res/values-tl/strings.xml
index 9d97d22..5a1090a 100644
--- a/packages/Keyguard/res/values-tl/strings.xml
+++ b/packages/Keyguard/res/values-tl/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Upang i-unlock, pindutin ang Menu pagkatapos ay 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Nalagpasan na ang maximum na mga pagtatangka sa Face Unlock"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Na-charge"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Nagcha-charge, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Nagtsa-charge"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Ikonekta ang iyong charger."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Pindutin ang Menu upang i-unlock."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Naka-lock ang network"</string>
diff --git a/packages/Keyguard/res/values-tr/strings.xml b/packages/Keyguard/res/values-tr/strings.xml
index 1d8b982..e2c1892 100644
--- a/packages/Keyguard/res/values-tr/strings.xml
+++ b/packages/Keyguard/res/values-tr/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Kilidi açmak için önce Menü\'ye, sonra 0\'a basın."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Yüz Tanıma Kilidi için maksimum deneme sayısı aşıldı"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Şarj oldu"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Şarj oluyor, <xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Şarj oluyor"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Şarj cihazınızı takın."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Kilidi açmak için Menü\'ye basın."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Ağ kilitli"</string>
diff --git a/packages/Keyguard/res/values-uk/strings.xml b/packages/Keyguard/res/values-uk/strings.xml
index 16cf6cf..519aa4d 100644
--- a/packages/Keyguard/res/values-uk/strings.xml
+++ b/packages/Keyguard/res/values-uk/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Щоб розбл., натисн. меню та 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Перевищено максимальну кількість спроб розблокування за допомогою функції \"Фейсконтроль\""</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Заряджено"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Заряджається, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Заряджається"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Підключіть зарядний пристрій."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Натисніть Меню, щоб розблокувати."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Мережу заблоковано"</string>
diff --git a/packages/Keyguard/res/values-vi/strings.xml b/packages/Keyguard/res/values-vi/strings.xml
index 00693aa5..58bda92 100644
--- a/packages/Keyguard/res/values-vi/strings.xml
+++ b/packages/Keyguard/res/values-vi/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Để mở khóa, hãy nhấn vào Trình đơn sau đó nhấn 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Đã vượt quá số lần Mở khóa bằng khuôn mặt tối đa"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Pin đầy"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Đang sạc, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Đang sạc"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Kết nối bộ sạc của bạn."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Nhấn vào Trình đơn để mở khóa."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Mạng đã bị khóa"</string>
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
index 1c014c3..09f521a 100644
--- a/packages/Keyguard/res/values-zh-rCN/strings.xml
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"要解锁,请先按 MENU 再按 0。"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超过“人脸解锁”尝试次数上限"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"充电完成"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"正在充电 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"正在充电"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"请连接充电器。"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"按“菜单”键解锁。"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"网络已锁定"</string>
diff --git a/packages/Keyguard/res/values-zh-rHK/strings.xml b/packages/Keyguard/res/values-zh-rHK/strings.xml
index 1b621d6..280dea3 100644
--- a/packages/Keyguard/res/values-zh-rHK/strings.xml
+++ b/packages/Keyguard/res/values-zh-rHK/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按選單鍵,然後按 0。"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超過臉容解鎖嘗試次數上限"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"充電完成"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"充電中 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"充電中"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"請連接充電器。"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"按選單鍵解鎖。"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"網絡已鎖定"</string>
diff --git a/packages/Keyguard/res/values-zh-rTW/strings.xml b/packages/Keyguard/res/values-zh-rTW/strings.xml
index 6e632ef..349282c 100644
--- a/packages/Keyguard/res/values-zh-rTW/strings.xml
+++ b/packages/Keyguard/res/values-zh-rTW/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"如要解鎖,請按 Menu 鍵,然後按 0。"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超過人臉解鎖嘗試次數上限"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"充電完成"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"充電中 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"充電中"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"連接充電器。"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"按選單鍵解鎖。"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"網路已鎖定"</string>
diff --git a/packages/Keyguard/res/values-zu/strings.xml b/packages/Keyguard/res/values-zu/strings.xml
index 95d3474..c69bc18 100644
--- a/packages/Keyguard/res/values-zu/strings.xml
+++ b/packages/Keyguard/res/values-zu/strings.xml
@@ -32,7 +32,7 @@
<string name="keyguard_label_text" msgid="861796461028298424">"Ukuvula, chofoza Menyu bese 0."</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Ukuzama Kokuvula Ubuso Okuningi kudluliwe"</string>
<string name="keyguard_charged" msgid="3272223906073492454">"Kushajiwe"</string>
- <string name="keyguard_plugged_in" msgid="8117572000639998388">"Iyashaja, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
+ <string name="keyguard_plugged_in" msgid="9087497435553252863">"Iyashaja"</string>
<string name="keyguard_low_battery" msgid="8143808018719173859">"Xhuma ishaja yakho."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"Chofoza imenyu ukuze uvule."</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"Inethiwekhi ikhiyiwe"</string>
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index e9cdfcd..f971522 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -163,4 +163,7 @@
<!-- The y translation to apply at the start in appear animations. -->
<dimen name="appear_y_translation_start">32dp</dimen>
+
+ <!-- The vertical margin between the date and the owner info. -->
+ <dimen name="date_owner_info_margin">2dp</dimen>
</resources>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index 8cf07fa..55d8d81 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -59,8 +59,8 @@
<string name="keyguard_charged">Charged</string>
<!-- When the lock screen is showing and the phone plugged in, and the battery
- is not fully charged, show the current charge %. -->
- <string name="keyguard_plugged_in">Charging, <xliff:g id="number">%d</xliff:g><xliff:g id="percent">%%</xliff:g></string>
+ is not fully charged, say that it's charging. -->
+ <string name="keyguard_plugged_in">Charging</string>
<!-- When the lock screen is showing and the battery is low, warn user to plug
in the phone soon. -->
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index d589283..9bc2a4d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -186,42 +186,16 @@
*/
void update() {
MutableInt icon = new MutableInt(0);
- CharSequence status = concat(getOwnerInfo(), getCurrentMessage());
+ CharSequence status = getCurrentMessage();
setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
setText(status);
}
- private CharSequence concat(CharSequence... args) {
- StringBuilder b = new StringBuilder();
- if (!TextUtils.isEmpty(args[0])) {
- b.append(args[0]);
- }
- for (int i = 1; i < args.length; i++) {
- CharSequence text = args[i];
- if (!TextUtils.isEmpty(text)) {
- if (b.length() > 0) {
- b.append(mSeparator);
- }
- b.append(text);
- }
- }
- return b.toString();
- }
CharSequence getCurrentMessage() {
return mShowingMessage ? mMessage : null;
}
- String getOwnerInfo() {
- ContentResolver res = getContext().getContentResolver();
- String info = null;
- final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
- if (ownerInfoEnabled && !mShowingMessage) {
- info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
- }
- return info;
- }
-
private void hideMessage(int duration, boolean thenUpdate) {
if (duration > 0) {
Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index e6de72f..a0b5536 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -475,7 +475,8 @@
interpolator, null);
// And the forgot pattern button
- if (mForgotPatternButton.getVisibility() == View.VISIBLE) {
+ if (mForgotPatternButton != null
+ && mForgotPatternButton.getVisibility() == View.VISIBLE) {
mAppearAnimationUtils.createAnimation(mForgotPatternButton, delay, duration,
startTranslationY, interpolator, null);
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
index 3f6ced6..bc159cb 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimpleHostView.java
@@ -25,6 +25,7 @@
public KeyguardSimpleHostView(Context context, AttributeSet attrs) {
super(context, attrs);
+ KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateCallback);
}
@Override
@@ -62,4 +63,10 @@
// TODO Auto-generated method stub
}
+ private KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ getSecurityContainer().showPrimarySecurityScreen(false /* turning off */);
+ }
+ };
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index bef94fa..a8b49e2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.text.TextUtils;
@@ -41,6 +42,7 @@
private TextView mAlarmStatusView;
private TextClock mDateView;
private TextClock mClockView;
+ private TextView mOwnerInfo;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -54,6 +56,7 @@
if (showing) {
if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing);
refresh();
+ updateOwnerInfo();
}
}
@@ -83,6 +86,7 @@
private void setEnableMarquee(boolean enabled) {
if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled);
+ mOwnerInfo.setSelected(enabled);
}
@Override
@@ -91,10 +95,12 @@
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
mDateView = (TextClock) findViewById(R.id.date_view);
mClockView = (TextClock) findViewById(R.id.clock_view);
+ mOwnerInfo = (TextView) findViewById(R.id.owner_info);
mLockPatternUtils = new LockPatternUtils(getContext());
final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
setEnableMarquee(screenOn);
refresh();
+ updateOwnerInfo();
// Disable elegant text height because our fancy colon makes the ymin value huge for no
// reason.
@@ -125,6 +131,21 @@
}
@Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ private void updateOwnerInfo() {
+ String ownerInfo = getOwnerInfo();
+ if (!TextUtils.isEmpty(ownerInfo)) {
+ mOwnerInfo.setVisibility(View.VISIBLE);
+ mOwnerInfo.setText(ownerInfo);
+ } else {
+ mOwnerInfo.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mInfoCallback);
@@ -140,6 +161,16 @@
return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
}
+ private String getOwnerInfo() {
+ ContentResolver res = getContext().getContentResolver();
+ String info = null;
+ final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
+ if (ownerInfoEnabled) {
+ info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
+ }
+ return info;
+ }
+
// DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
// This is an optimization to ensure we only recompute the patterns when the inputs change.
private static final class Patterns {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index fef971c..668e1ef 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -418,7 +418,7 @@
}
}
- /* package */ static class BatteryStatus {
+ public static class BatteryStatus {
public final int status;
public final int level;
public final int plugged;
diff --git a/packages/Keyguard/test/SampleTrustAgent/Android.mk b/packages/Keyguard/test/SampleTrustAgent/Android.mk
index 7551fdf..2a18ee1 100644
--- a/packages/Keyguard/test/SampleTrustAgent/Android.mk
+++ b/packages/Keyguard/test/SampleTrustAgent/Android.mk
@@ -20,9 +20,8 @@
LOCAL_PACKAGE_NAME := SampleTrustAgent
-# Remove these to verify permission checks are working correctly
-#LOCAL_CERTIFICATE := platform
-#LOCAL_PRIVILEGED_MODULE := true
+# Remove this to verify permission checks are working correctly
+LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := tests
diff --git a/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml b/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
index 7904927..f3125f1 100644
--- a/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
+++ b/packages/Keyguard/test/SampleTrustAgent/AndroidManifest.xml
@@ -18,6 +18,7 @@
package="com.android.trustagent.test">
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17"/>
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+ <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
<application android:label="@string/app_name">
<service
android:name=".SampleTrustAgent"
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index 9e7b969..96592b4 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -24,4 +24,6 @@
LOCAL_JAVA_LIBRARIES := framework-base
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+
include $(BUILD_PACKAGE)
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index e1d0aec..4c0bbb8 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -42,23 +42,23 @@
<uses-permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"/>
<application
- android:allowClearUserData="true"
- android:label="@string/app_label"
- android:allowBackup= "false"
- android:supportsRtl="true"
- android:icon="@*android:drawable/ic_print">
+ android:allowClearUserData="true"
+ android:label="@string/app_label"
+ android:allowBackup= "false"
+ android:supportsRtl="true"
+ android:icon="@*android:drawable/ic_print">
<service
- android:name=".PrintSpoolerService"
+ android:name=".model.PrintSpoolerService"
android:exported="true"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE">
</service>
<activity
- android:name=".PrintJobConfigActivity"
+ android:name=".ui.PrintActivity"
android:configChanges="orientation|screenSize"
android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
- android:theme="@style/PrintJobConfigActivityTheme">
+ android:theme="@android:style/Theme.DeviceDefault.NoActionBar">
<intent-filter>
<action android:name="android.print.PRINT_DIALOG" />
<category android:name="android.intent.category.DEFAULT" />
@@ -67,7 +67,7 @@
</activity>
<activity
- android:name=".SelectPrinterActivity"
+ android:name=".ui.SelectPrinterActivity"
android:label="@string/all_printers_label"
android:theme="@style/SelectPrinterActivityTheme"
android:exported="false">
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less_24dp.png
new file mode 100644
index 0000000..d2e5408
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_less_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more_24dp.png
new file mode 100644
index 0000000..f4c4b0c
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/ic_expand_more_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png
new file mode 100644
index 0000000..5e54970
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/ic_grayedout_printer.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-hdpi/print_button_background.png b/packages/PrintSpooler/res/drawable-hdpi/print_button_background.png
new file mode 100644
index 0000000..88e8495
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-hdpi/print_button_background.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png
new file mode 100644
index 0000000..3220eea
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_less_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png
new file mode 100644
index 0000000..5530f52
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/ic_expand_more_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png
new file mode 100644
index 0000000..5e54970
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/ic_grayedout_printer.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-mdpi/print_button_background.png b/packages/PrintSpooler/res/drawable-mdpi/print_button_background.png
new file mode 100644
index 0000000..3a37b27
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-mdpi/print_button_background.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less_24dp.png
new file mode 100644
index 0000000..f0074275
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_less_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more_24dp.png
new file mode 100644
index 0000000..43debb3
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/ic_expand_more_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png b/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png
new file mode 100644
index 0000000..5e54970
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/ic_grayedout_printer.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xhdpi/print_button_background.png b/packages/PrintSpooler/res/drawable-xhdpi/print_button_background.png
new file mode 100644
index 0000000..b2ed8cd
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xhdpi/print_button_background.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less_24dp.png
new file mode 100644
index 0000000..39bc2ba
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_less_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more_24dp.png
new file mode 100644
index 0000000..664f3f2
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxhdpi/ic_expand_more_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less_24dp.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less_24dp.png
new file mode 100644
index 0000000..fe9c539
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_less_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more_24dp.png b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more_24dp.png
new file mode 100644
index 0000000..18d075c
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable-xxxhdpi/ic_expand_more_24dp.png
Binary files differ
diff --git a/packages/PrintSpooler/res/drawable/ic_expand_less.xml b/packages/PrintSpooler/res/drawable/ic_expand_less.xml
new file mode 100644
index 0000000..b0c7d51
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/ic_expand_less.xml
@@ -0,0 +1,43 @@
+<?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"
+ android:autoMirrored="true">
+
+ <item
+ android:state_checked="true">
+ <bitmap
+ android:src="@drawable/ic_expand_less_24dp"
+ android:tint="?android:attr/colorControlActivated">
+ </bitmap>
+ </item>
+
+ <item
+ android:state_pressed="true">
+ <bitmap
+ android:src="@drawable/ic_expand_less_24dp"
+ android:tint="?android:attr/colorControlActivated">
+ </bitmap>
+ </item>
+
+ <item>
+ <bitmap
+ android:src="@drawable/ic_expand_less_24dp"
+ android:tint="?android:attr/colorControlNormal">
+ </bitmap>
+ </item>
+
+</selector>
diff --git a/packages/PrintSpooler/res/drawable/ic_expand_more.xml b/packages/PrintSpooler/res/drawable/ic_expand_more.xml
new file mode 100644
index 0000000..b809c25
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/ic_expand_more.xml
@@ -0,0 +1,43 @@
+<?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"
+ android:autoMirrored="true">
+
+ <item
+ android:state_checked="true">
+ <bitmap
+ android:src="@drawable/ic_expand_more_24dp"
+ android:tint="?android:attr/colorControlActivated">
+ </bitmap>
+ </item>
+
+ <item
+ android:state_pressed="true">
+ <bitmap
+ android:src="@drawable/ic_expand_more_24dp"
+ android:tint="?android:attr/colorControlActivated">
+ </bitmap>
+ </item>
+
+ <item>
+ <bitmap
+ android:src="@drawable/ic_expand_more_24dp"
+ android:tint="?android:attr/colorControlNormal">
+ </bitmap>
+ </item>
+
+</selector>
diff --git a/packages/PrintSpooler/res/drawable/print_button.xml b/packages/PrintSpooler/res/drawable/print_button.xml
new file mode 100644
index 0000000..b59afba
--- /dev/null
+++ b/packages/PrintSpooler/res/drawable/print_button.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/print_button_tint_color">
+ <item
+ android:drawable="@drawable/print_button_background">
+ </item>
+</ripple>
diff --git a/packages/PrintSpooler/res/layout/print_activity.xml b/packages/PrintSpooler/res/layout/print_activity.xml
new file mode 100644
index 0000000..9715322
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/print_activity.xml
@@ -0,0 +1,389 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<com.android.printspooler.widget.ContentView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:printspooler="http://schemas.android.com/apk/res/com.android.printspooler"
+ android:id="@+id/options_content"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:visibility="invisible"
+ android:background="?android:attr/colorForeground">
+
+ <FrameLayout
+ android:id="@+id/static_content"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dip"
+ android:background="?android:attr/colorForegroundInverse">
+
+ <!-- Destination -->
+
+ <Spinner
+ android:id="@+id/destination_spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:dropDownWidth="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall">
+ </Spinner>
+
+ </FrameLayout>
+
+ <!-- Summary -->
+
+ <LinearLayout
+ android:id="@+id/summary_content"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:orientation="horizontal"
+ android:background="?android:attr/colorForegroundInverse">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="12dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:labelFor="@+id/copies_count_summary"
+ android:text="@string/label_copies_summary">
+ </TextView>
+
+ <TextView
+ android:id="@+id/copies_count_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="16dip"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="32dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:labelFor="@+id/paper_size_summary"
+ android:text="@string/label_paper_size_summary">
+ </TextView>
+
+ <TextView
+ android:id="@+id/paper_size_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="16dip"
+ android:textAppearance="?android:attr/textAppearanceMedium">
+ </TextView>
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/dynamic_content"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="16dip">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/draggable_content"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <com.android.printspooler.widget.PrintOptionsLayout
+ android:id="@+id/options_container"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorForegroundInverse"
+ printspooler:columnCount="@integer/print_option_column_count">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:orientation="vertical">
+
+ <!-- Copies -->
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="12dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:labelFor="@+id/copies_edittext"
+ android:text="@string/label_copies">
+ </TextView>
+
+ <view
+ class="com.android.printspooler.widget.FirstFocusableEditText"
+ android:id="@+id/copies_edittext"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="?android:attr/editTextStyle"
+ android:inputType="numberDecimal">
+ </view>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:orientation="vertical">
+
+ <!-- Paper size -->
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="12dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:labelFor="@+id/paper_size_spinner"
+ android:text="@string/label_paper_size">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/paper_size_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@style/PrintOptionSpinnerStyle">
+ </Spinner>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:orientation="vertical">
+
+ <!-- Color -->
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="12dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:labelFor="@+id/color_spinner"
+ android:text="@string/label_color">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/color_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@style/PrintOptionSpinnerStyle">
+ </Spinner>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:orientation="vertical">
+
+ <!-- Orientation -->
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="12dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:labelFor="@+id/orientation_spinner"
+ android:text="@string/label_orientation">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/orientation_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@style/PrintOptionSpinnerStyle">
+ </Spinner>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:orientation="vertical">
+
+ <!-- Range options -->
+
+ <TextView
+ android:id="@+id/range_options_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="12dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:labelFor="@+id/range_options_spinner"
+ android:text="@string/page_count_unknown">
+ </TextView>
+
+ <Spinner
+ android:id="@+id/range_options_spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@style/PrintOptionSpinnerStyle">
+ </Spinner>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dip"
+ android:layout_marginEnd="16dip"
+ android:orientation="vertical">
+
+ <!-- Pages -->
+
+ <TextView
+ android:id="@+id/page_range_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:layout_marginStart="12dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@string/pages_range_example"
+ android:labelFor="@+id/page_range_edittext"
+ android:textAllCaps="false"
+ android:visibility="visible">
+ </TextView>
+
+ <view
+ class="com.android.printspooler.widget.FirstFocusableEditText"
+ android:id="@+id/page_range_edittext"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|fill_horizontal"
+ style="@style/PrintOptionEditTextStyle"
+ android:visibility="visible"
+ android:inputType="textNoSuggestions">
+ </view>
+
+ </LinearLayout>
+
+ </com.android.printspooler.widget.PrintOptionsLayout>
+
+ <!-- More options -->
+
+ <LinearLayout
+ android:id="@+id/more_options_container"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="28dip"
+ android:paddingEnd="28dip"
+ android:orientation="vertical"
+ android:visibility="visible"
+ android:background="?android:attr/colorForegroundInverse">
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:layout_gravity="fill_horizontal"
+ android:background="?android:attr/colorControlNormal"
+ android:contentDescription="@null">
+ </ImageView>
+
+ <Button
+ android:id="@+id/more_options_button"
+ style="?android:attr/borderlessButtonStyle"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="fill_horizontal"
+ android:text="@string/more_options_button"
+ android:gravity="start|center_vertical"
+ android:textAllCaps="false">
+ </Button>
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:layout_gravity="fill_horizontal"
+ android:background="?android:attr/colorControlNormal"
+ android:contentDescription="@null">
+ </ImageView>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <!-- Expand/collapse handle -->
+
+ <FrameLayout
+ android:id="@+id/expand_collapse_handle"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:layout_marginBottom="28dip"
+ android:background="?android:attr/colorForegroundInverse"
+ android:elevation="12dip">
+
+ <ImageButton
+ android:id="@+id/expand_collapse_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@drawable/ic_expand_more">
+ </ImageButton>
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <!-- Print button -->
+
+ <ImageButton
+ android:id="@+id/print_button"
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end|bottom"
+ android:layout_marginEnd="16dip"
+ android:elevation="12dip"
+ android:background="@drawable/print_button"
+ android:src="@*android:drawable/ic_print">
+ </ImageButton>
+
+ </FrameLayout>
+
+
+ <FrameLayout
+ android:id="@+id/embedded_content_container"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:animateLayoutChanges="true">
+ </FrameLayout>
+
+</com.android.printspooler.widget.ContentView>
diff --git a/packages/PrintSpooler/res/layout/print_error_fragment.xml b/packages/PrintSpooler/res/layout/print_error_fragment.xml
new file mode 100644
index 0000000..dc44339
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/print_error_fragment.xml
@@ -0,0 +1,47 @@
+<?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.
+ -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:src="@drawable/ic_grayedout_printer"
+ android:contentDescription="@null">
+ </ImageView>
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/print_error_default_message"
+ android:textAppearance="?android:attr/textAppearanceLargeInverse">
+ </TextView>
+
+ <Button
+ android:id="@+id/action_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/print_error_retry">
+ </Button>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
deleted file mode 100644
index 3303ef1..0000000
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_container.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<com.android.printspooler.PrintDialogFrame xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <FrameLayout
- android:id="@+id/content_container"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:background="@color/container_background">
- </FrameLayout>
-</com.android.printspooler.PrintDialogFrame>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml
deleted file mode 100644
index e50a7af..0000000
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_editing.xml
+++ /dev/null
@@ -1,282 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:scrollbars="vertical">
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <!-- Destination -->
-
- <Spinner
- android:id="@+id/destination_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_horizontal"
- android:layout_marginTop="24dip"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip"
- android:minHeight="?android:attr/listPreferredItemHeightSmall">
- </Spinner>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="24dip"
- android:orientation="horizontal"
- android:baselineAligned="false">
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical">
-
- <!-- Copies -->
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dip"
- android:layout_marginStart="36dip"
- android:layout_marginEnd="6dip"
- android:textAppearance="@style/PrintOptionTitleTextAppearance"
- android:labelFor="@+id/copies_edittext"
- android:text="@string/label_copies">
- </TextView>
-
- <view
- class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
- android:id="@+id/copies_edittext"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="6dip"
- style="@style/PrintOptionEditTextStyle"
- android:inputType="numberDecimal">
- </view>
-
- <!-- Color -->
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dip"
- android:layout_marginStart="36dip"
- android:layout_marginEnd="6dip"
- android:textAppearance="@style/PrintOptionTitleTextAppearance"
- android:labelFor="@+id/color_spinner"
- android:text="@string/label_color">
- </TextView>
-
- <Spinner
- android:id="@+id/color_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="6dip"
- style="@style/PrintOptionSpinnerStyle">
- </Spinner>
-
- <!-- Range options -->
-
- <TextView
- android:id="@+id/range_options_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dip"
- android:layout_marginStart="36dip"
- android:textAppearance="@style/PrintOptionTitleTextAppearance"
- android:labelFor="@+id/range_options_spinner"
- android:text="@string/page_count_unknown">
- </TextView>
-
- <Spinner
- android:id="@+id/range_options_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="6dip"
- style="@style/PrintOptionSpinnerStyle">
- </Spinner>
-
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical">
-
- <!-- Paper size -->
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dip"
- android:layout_marginStart="18dip"
- android:layout_marginEnd="24dip"
- android:textAppearance="@style/PrintOptionTitleTextAppearance"
- android:labelFor="@+id/paper_size_spinner"
- android:text="@string/label_paper_size">
- </TextView>
-
- <Spinner
- android:id="@+id/paper_size_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="6dip"
- android:layout_marginEnd="24dip"
- style="@style/PrintOptionSpinnerStyle">
- </Spinner>
-
- <!-- Orientation -->
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dip"
- android:layout_marginStart="18dip"
- android:layout_marginEnd="24dip"
- android:textAppearance="@style/PrintOptionTitleTextAppearance"
- android:labelFor="@+id/orientation_spinner"
- android:text="@string/label_orientation">
- </TextView>
-
- <Spinner
- android:id="@+id/orientation_spinner"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="6dip"
- android:layout_marginEnd="24dip"
- style="@style/PrintOptionSpinnerStyle">
- </Spinner>
-
- <!-- Pages -->
-
- <TextView
- android:id="@+id/page_range_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="12dip"
- android:layout_marginStart="12dip"
- android:layout_marginEnd="24dip"
- android:textAppearance="@style/PrintOptionTitleTextAppearance"
- android:text="@string/pages_range_example"
- android:labelFor="@+id/page_range_edittext"
- android:textAllCaps="false">
- </TextView>
-
- <view
- class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
- android:id="@+id/page_range_edittext"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="6dip"
- android:layout_marginEnd="24dip"
- android:layout_gravity="bottom|fill_horizontal"
- style="@style/PrintOptionEditTextStyle"
- android:visibility="gone"
- android:inputType="textNoSuggestions">
- </view>
-
- </LinearLayout>
-
- </LinearLayout>
-
- <!-- Advanced settings button -->
-
- <LinearLayout
- android:id="@+id/advanced_settings_container"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
-
- <ImageView
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip"
- android:layout_gravity="fill_horizontal"
- android:background="@color/separator"
- android:contentDescription="@null">
- </ImageView>
-
- <Button
- android:id="@+id/advanced_settings_button"
- style="?android:attr/buttonBarButtonStyle"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip"
- android:layout_gravity="fill_horizontal"
- android:text="@string/advanced_settings_button"
- android:gravity="start|center_vertical"
- android:textSize="16sp"
- android:textColor="@color/item_text_color">
- </Button>
-
- <ImageView
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:layout_gravity="fill_horizontal"
- android:layout_marginStart="24dip"
- android:layout_marginEnd="24dip"
- android:background="@color/separator"
- android:contentDescription="@null">
- </ImageView>
-
- </LinearLayout>
-
- <!-- Print button -->
-
- <FrameLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dip"
- android:background="@color/action_button_background">
-
- <ImageView
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:layout_gravity="fill_horizontal"
- android:background="@color/separator"
- android:contentDescription="@null">
- </ImageView>
-
- <Button
- android:id="@+id/print_button"
- style="?android:attr/buttonBarButtonStyle"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_horizontal"
- android:text="@string/print_button"
- android:textSize="16sp"
- android:textColor="@color/item_text_color">
- </Button>
-
- </FrameLayout>
-
- </LinearLayout>
-
-</ScrollView>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
deleted file mode 100644
index d9f0a9a..0000000
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_error.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/content_generating"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dip"
- android:layout_marginEnd="16dip"
- android:layout_marginTop="32dip"
- android:layout_marginBottom="32dip"
- android:layout_gravity="center"
- style="?android:attr/buttonBarButtonStyle"
- android:ellipsize="end"
- android:text="@string/print_error_default_message"
- android:textColor="@color/important_text"
- android:textSize="16sp">
- </TextView>
-
- </LinearLayout>
-
- <FrameLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@color/action_button_background">
-
- <View
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:background="@color/separator">
- </View>
-
- <Button
- android:id="@+id/ok_button"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_horizontal"
- style="?android:attr/buttonBarButtonStyle"
- android:text="@android:string/ok"
- android:textSize="16sp"
- android:textColor="@color/important_text">
- </Button>
-
- </FrameLayout>
-
-</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml b/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml
deleted file mode 100644
index 10602ee..0000000
--- a/packages/PrintSpooler/res/layout/print_job_config_activity_content_generating.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/content_generating"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dip"
- android:layout_marginEnd="16dip"
- android:layout_gravity="center"
- style="?android:attr/buttonBarButtonStyle"
- android:singleLine="true"
- android:ellipsize="end"
- android:text="@string/generating_print_job"
- android:textColor="@color/important_text"
- android:textSize="16sp">
- </TextView>
-
- <ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="32dip"
- android:layout_marginEnd="32dip"
- android:layout_marginTop="16dip"
- android:layout_marginBottom="32dip"
- android:layout_gravity="center_horizontal"
- style="?android:attr/progressBarStyleLarge">
- </ProgressBar>
-
- </LinearLayout>
-
- <FrameLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:background="@color/action_button_background">
-
- <View
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:background="@color/separator">
- </View>
-
- <Button
- android:id="@+id/cancel_button"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="fill_horizontal"
- style="?android:attr/buttonBarButtonStyle"
- android:text="@string/cancel"
- android:textSize="16sp"
- android:textColor="@color/important_text">
- </Button>
-
- </FrameLayout>
-
-</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/print_progress_fragment.xml b/packages/PrintSpooler/res/layout/print_progress_fragment.xml
new file mode 100644
index 0000000..212da9e
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/print_progress_fragment.xml
@@ -0,0 +1,65 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dip"
+ android:src="@drawable/ic_grayedout_printer"
+ android:contentDescription="@null">
+ </ImageView>
+
+ <ProgressBar
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:indeterminate="true"
+ style="?android:attr/progressBarStyleHorizontal">
+ </ProgressBar>
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center"
+ android:animateLayoutChanges="true">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLargeInverse"
+ android:text="@string/print_operation_canceling"
+ android:visibility="gone">
+ </TextView>
+
+ <Button
+ android:id="@+id/cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@android:string/cancel">
+ </Button>
+
+ </FrameLayout>
+
+</LinearLayout>
+
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index 1a61b99..43d8aaf 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -19,7 +19,7 @@
android:layout_height="wrap_content"
android:paddingStart="16dip"
android:paddingEnd="16dip"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:minHeight="56dip"
android:orientation="horizontal"
android:gravity="start|center_vertical">
@@ -49,7 +49,7 @@
android:ellipsize="end"
android:textIsSelectable="false"
android:gravity="top|start"
- android:textColor="@color/item_text_color"
+ android:textColor="?android:attr/textColorPrimary"
android:duplicateParentState="true">
</TextView>
@@ -62,7 +62,7 @@
android:ellipsize="end"
android:textIsSelectable="false"
android:visibility="gone"
- android:textColor="@color/print_option_title"
+ android:textColor="?android:attr/textColorPrimary"
android:duplicateParentState="true">
</TextView>
diff --git a/packages/PrintSpooler/res/layout/printer_list_item.xml b/packages/PrintSpooler/res/layout/printer_list_item.xml
index 47eb0b5..1f5efbc 100644
--- a/packages/PrintSpooler/res/layout/printer_list_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_list_item.xml
@@ -15,13 +15,13 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
- android:gravity="start|center_vertical">
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
<ImageView
android:id="@+id/icon"
@@ -31,7 +31,7 @@
android:layout_marginEnd="8dip"
android:duplicateParentState="true"
android:contentDescription="@null"
- android:visibility="gone">
+ android:visibility="invisible">
</ImageView>
<LinearLayout
@@ -49,7 +49,7 @@
android:ellipsize="end"
android:textIsSelectable="false"
android:gravity="top|start"
- android:textColor="@color/item_text_color"
+ android:textColor="?android:attr/textColorSecondary"
android:duplicateParentState="true">
</TextView>
@@ -62,7 +62,7 @@
android:ellipsize="end"
android:textIsSelectable="false"
android:visibility="gone"
- android:textColor="@color/print_option_title"
+ android:textColor="?android:attr/textColorSecondary"
android:duplicateParentState="true">
</TextView>
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 4488b6a..173057b 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -19,12 +19,16 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent">
- <fragment
- android:name="com.android.printspooler.SelectPrinterFragment"
- android:id="@+id/select_printer_fragment"
+ <ListView
+ android:id="@android:id/list"
android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- </fragment>
+ android:layout_height="fill_parent"
+ android:paddingStart="@dimen/printer_list_view_padding_start"
+ android:paddingEnd="@dimen/printer_list_view_padding_end"
+ android:scrollbarStyle="outsideOverlay"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true" >
+ </ListView>
<FrameLayout
android:id="@+id/empty_print_state"
diff --git a/packages/PrintSpooler/res/layout/select_printer_fragment.xml b/packages/PrintSpooler/res/layout/select_printer_fragment.xml
deleted file mode 100644
index bbd012e..0000000
--- a/packages/PrintSpooler/res/layout/select_printer_fragment.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<ListView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/list"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:paddingStart="@dimen/printer_list_view_padding_start"
- android:paddingEnd="@dimen/printer_list_view_padding_end"
- android:scrollbarStyle="outsideOverlay"
- android:cacheColorHint="@android:color/transparent"
- android:scrollbarAlwaysDrawVerticalTrack="true" >
-</ListView>
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
index c3c5021..1fb221a 100644
--- a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
@@ -32,7 +32,7 @@
android:ellipsize="end"
android:textIsSelectable="false"
android:gravity="top|left"
- android:textColor="@color/item_text_color"
+ android:textColor="?android:attr/textColorPrimary"
android:duplicateParentState="true">
</TextView>
@@ -45,7 +45,7 @@
android:ellipsize="end"
android:textIsSelectable="false"
android:visibility="gone"
- android:textColor="@color/print_option_title"
+ android:textColor="?android:attr/textColorPrimary"
android:duplicateParentState="true">
</TextView>
diff --git a/packages/PrintSpooler/res/values-af/strings.xml b/packages/PrintSpooler/res/values-af/strings.xml
index a10a5a0..876dc01 100644
--- a/packages/PrintSpooler/res/values-af/strings.xml
+++ b/packages/PrintSpooler/res/values-af/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Drukwaglys"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Drukkerinstellings"</string>
- <string name="print_button" msgid="645164566271246268">"Druk"</string>
- <string name="save_button" msgid="1921310454071758999">"Stoor"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Bestemming"</string>
<string name="label_copies" msgid="3634531042822968308">"Afskrifte"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Papiergrootte"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Kleur"</string>
<string name="label_orientation" msgid="2853142581990496477">"Oriëntasie"</string>
<string name="label_pages" msgid="6300874667546617333">"Bladsye (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met drukker nie"</string>
<string name="reason_unknown" msgid="5507940196503246139">"onbekend"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie beskikbaar nie"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Kon nie uitdruktaak genereer nie"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Swart en wit"</item>
<item msgid="2762241247228983754">"Kleur"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Alles"</item>
<item msgid="6812869625222503603">"Reikwydte"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Kon nie uitdruktaak genereer nie"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index be64c95..b7f1d7f 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"የህትመት አስተላላፊ"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"የአታሚ ቅንብሮች"</string>
- <string name="print_button" msgid="645164566271246268">"አትም"</string>
- <string name="save_button" msgid="1921310454071758999">"አስቀምጥ"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"መድረሻ"</string>
<string name="label_copies" msgid="3634531042822968308">"ቅጂዎች"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"የወረቀት መጠን"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"ቀለም"</string>
<string name="label_orientation" msgid="2853142581990496477">"አቀማመጠ ገፅ"</string>
<string name="label_pages" msgid="6300874667546617333">"ገጾች (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ከአታሚ ጋር ምንም ግንኙነት የለም"</string>
<string name="reason_unknown" msgid="5507940196503246139">"አይታወቅም"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – አይገኝም"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"የህትመት ስራን ማመንጨት አልተቻለም"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ጥቁር እና ነጭ"</item>
<item msgid="2762241247228983754">"ቀለም"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"ሁሉም"</item>
<item msgid="6812869625222503603">"ምጥጥነ ገጽታ"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"የህትመት ስራን ማመንጨት አልተቻለም"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-ar/strings.xml b/packages/PrintSpooler/res/values-ar/strings.xml
index b883f93..ca6100b 100644
--- a/packages/PrintSpooler/res/values-ar/strings.xml
+++ b/packages/PrintSpooler/res/values-ar/strings.xml
@@ -17,12 +17,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"إعدادات الطابعة"</string>
- <string name="print_button" msgid="645164566271246268">"طباعة"</string>
- <string name="save_button" msgid="1921310454071758999">"حفظ"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"خيارات أخرى"</string>
<string name="label_destination" msgid="9132510997381599275">"الوجهة"</string>
<string name="label_copies" msgid="3634531042822968308">"عدد النسخ"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"حجم الورق"</string>
+ <string name="label_copies_summary" msgid="3861966063536529540">"النُسخ:"</string>
+ <string name="label_paper_size" msgid="908654383827777759">"حجم الورق"</string>
+ <string name="label_paper_size_summary" msgid="5668204981332138168">"حجم الورق:"</string>
<string name="label_color" msgid="1108690305218188969">"ألوان"</string>
<string name="label_orientation" msgid="2853142581990496477">"الاتجاه"</string>
<string name="label_pages" msgid="6300874667546617333">"الصفحات (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +63,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"لا يوجد اتصال بالطابعة"</string>
<string name="reason_unknown" msgid="5507940196503246139">"غير معروف"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – غير متاحة"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"تعذر إنشاء عملية الطباعة"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"أبيض وأسود"</item>
<item msgid="2762241247228983754">"ملونة"</item>
@@ -76,4 +75,9 @@
<item msgid="7421377442011699994">"الكل"</item>
<item msgid="6812869625222503603">"النطاق"</item>
</string-array>
+ <string name="print_write_error_message" msgid="5787642615179572543">"تعذرت الكتابة إلى الملف"</string>
+ <string name="print_error_default_message" msgid="8568506918983980567">"تعذر إنشاء عملية الطباعة"</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"إعادة المحاولة"</string>
+ <string name="print_error_printer_unavailable" msgid="6653128543854282851">"الطابعة غير متاحة"</string>
+ <string name="print_operation_canceling" msgid="5274571823242489160">"جارٍ الإلغاء…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-bg/strings.xml b/packages/PrintSpooler/res/values-bg/strings.xml
index 4009aa2..b1a41e6 100644
--- a/packages/PrintSpooler/res/values-bg/strings.xml
+++ b/packages/PrintSpooler/res/values-bg/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Спулер за печат"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Настройки на принтера"</string>
- <string name="print_button" msgid="645164566271246268">"Печат"</string>
- <string name="save_button" msgid="1921310454071758999">"Запазване"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Местоназначение"</string>
<string name="label_copies" msgid="3634531042822968308">"Копия"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Размер на хартията"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Цвят"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
<string name="label_pages" msgid="6300874667546617333">"Страници (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Няма връзка с принтера"</string>
<string name="reason_unknown" msgid="5507940196503246139">"няма данни"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – не е налице"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Заданието за отпечатване не можа да се генерира"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Черно-бяло"</item>
<item msgid="2762241247228983754">"Цветно"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Всички"</item>
<item msgid="6812869625222503603">"Поредица"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Заданието за отпечатване не можа да се генерира"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-ca/strings.xml b/packages/PrintSpooler/res/values-ca/strings.xml
index a429dd5..c553a96 100644
--- a/packages/PrintSpooler/res/values-ca/strings.xml
+++ b/packages/PrintSpooler/res/values-ca/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Gest. cues impr."</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Configuració impressora"</string>
- <string name="print_button" msgid="645164566271246268">"Imprimeix"</string>
- <string name="save_button" msgid="1921310454071758999">"Desa"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destinació"</string>
<string name="label_copies" msgid="3634531042822968308">"Còpies"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Mida del paper"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Color"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientació"</string>
<string name="label_pages" msgid="6300874667546617333">"Pàgines (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hi ha connexió amb la impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconegut"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"No s\'ha pogut generar la tasca d\'impressió"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Blanc i negre"</item>
<item msgid="2762241247228983754">"Color"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Tots"</item>
<item msgid="6812869625222503603">"Interval"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"No s\'ha pogut generar la tasca d\'impressió"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-cs/strings.xml b/packages/PrintSpooler/res/values-cs/strings.xml
index 3ddc701..aff3e95 100644
--- a/packages/PrintSpooler/res/values-cs/strings.xml
+++ b/packages/PrintSpooler/res/values-cs/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Nastavení tiskárny"</string>
- <string name="print_button" msgid="645164566271246268">"Tisk"</string>
- <string name="save_button" msgid="1921310454071758999">"Uložit"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Cíl"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopie"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Velikost papíru"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Barva"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientace"</string>
<string name="label_pages" msgid="6300874667546617333">"STRÁNKY (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nelze se připojit k tiskárně"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznámé"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – není k dispozici"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Tiskovou úlohu nelze vytvořit"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Černobíle"</item>
<item msgid="2762241247228983754">"Barevně"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Vše"</item>
<item msgid="6812869625222503603">"Rozsah"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Tiskovou úlohu nelze vytvořit"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-da/strings.xml b/packages/PrintSpooler/res/values-da/strings.xml
index 1a871f8..54c9f22 100644
--- a/packages/PrintSpooler/res/values-da/strings.xml
+++ b/packages/PrintSpooler/res/values-da/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Printerindstillinger"</string>
- <string name="print_button" msgid="645164566271246268">"Udskriv"</string>
- <string name="save_button" msgid="1921310454071758999">"Gem"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destination"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopier"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Papirstørrelse"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Farve"</string>
<string name="label_orientation" msgid="2853142581990496477">"Retning"</string>
<string name="label_pages" msgid="6300874667546617333">"Sider (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse til printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ukendt"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ikke tilgængelig"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Der kunne ikke genereres et udskriftsjob"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Sort/hvid"</item>
<item msgid="2762241247228983754">"Farve"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Alle"</item>
<item msgid="6812869625222503603">"Interval"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Der kunne ikke genereres et udskriftsjob"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-de/strings.xml b/packages/PrintSpooler/res/values-de/strings.xml
index 6b83ac3..321c709 100644
--- a/packages/PrintSpooler/res/values-de/strings.xml
+++ b/packages/PrintSpooler/res/values-de/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Druck-Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Druckereinstellungen"</string>
- <string name="print_button" msgid="645164566271246268">"Drucken"</string>
- <string name="save_button" msgid="1921310454071758999">"Speichern"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Ziel"</string>
<string name="label_copies" msgid="3634531042822968308">"Exemplare"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Papiergröße"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Farbe"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ausrichtung"</string>
<string name="label_pages" msgid="6300874667546617333">"Seiten (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Keine Verbindung zum Drucker"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unbekannt"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nicht verfügbar"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Druckauftrag konnte nicht generiert werden."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Schwarz-weiß"</item>
<item msgid="2762241247228983754">"Farbe"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Alle"</item>
<item msgid="6812869625222503603">"Bereich"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Druckauftrag konnte nicht generiert werden."</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-el/strings.xml b/packages/PrintSpooler/res/values-el/strings.xml
index 795e730..542caf9 100644
--- a/packages/PrintSpooler/res/values-el/strings.xml
+++ b/packages/PrintSpooler/res/values-el/strings.xml
@@ -17,12 +17,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Λογισμικό ουράς εκτύπωσης"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Ρυθμίσεις εκτυπωτή"</string>
- <string name="print_button" msgid="645164566271246268">"Εκτύπωση"</string>
- <string name="save_button" msgid="1921310454071758999">"Αποθήκευση"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"Περισσότερες επιλογές"</string>
<string name="label_destination" msgid="9132510997381599275">"Προορισμός"</string>
<string name="label_copies" msgid="3634531042822968308">"Αντίγραφα"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Μέγεθος χαρτιού"</string>
+ <string name="label_copies_summary" msgid="3861966063536529540">"Αντίγραφα:"</string>
+ <string name="label_paper_size" msgid="908654383827777759">"Μεγέθος χαρτιού"</string>
+ <string name="label_paper_size_summary" msgid="5668204981332138168">"Μέγεθος χαρτιού:"</string>
<string name="label_color" msgid="1108690305218188969">"Χρώμα"</string>
<string name="label_orientation" msgid="2853142581990496477">"Προσανατολισμός"</string>
<string name="label_pages" msgid="6300874667546617333">"Σελίδες (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +63,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Δεν υπάρχει σύνδεση με εκτυπωτή"</string>
<string name="reason_unknown" msgid="5507940196503246139">"άγνωστο"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – μη διαθέσιμο"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Δεν ήταν δυνατή η δημιουργία εργασίας εκτύπωσης"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Ασπρόμαυρο"</item>
<item msgid="2762241247228983754">"Χρώμα"</item>
@@ -76,4 +75,9 @@
<item msgid="7421377442011699994">"Όλα"</item>
<item msgid="6812869625222503603">"Εύρος"</item>
</string-array>
+ <string name="print_write_error_message" msgid="5787642615179572543">"Δεν ήταν δυνατή η εγγραφή στο αρχείο"</string>
+ <string name="print_error_default_message" msgid="8568506918983980567">"Δεν ήταν δυνατή η δημιουργία εργασίας εκτύπωσης"</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"Επανάληψη"</string>
+ <string name="print_error_printer_unavailable" msgid="6653128543854282851">"Ο εκτυπωτής δεν είναι διαθέσιμος"</string>
+ <string name="print_operation_canceling" msgid="5274571823242489160">"Ακύρωση…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-en-rGB/strings.xml b/packages/PrintSpooler/res/values-en-rGB/strings.xml
index 27372f8..3728437 100644
--- a/packages/PrintSpooler/res/values-en-rGB/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rGB/strings.xml
@@ -17,12 +17,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Printer settings"</string>
- <string name="print_button" msgid="645164566271246268">"Print"</string>
- <string name="save_button" msgid="1921310454071758999">"Save"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"More options"</string>
<string name="label_destination" msgid="9132510997381599275">"Destination"</string>
<string name="label_copies" msgid="3634531042822968308">"Copies"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Paper Size"</string>
+ <string name="label_copies_summary" msgid="3861966063536529540">"Copies:"</string>
+ <string name="label_paper_size" msgid="908654383827777759">"Paper size"</string>
+ <string name="label_paper_size_summary" msgid="5668204981332138168">"Paper size:"</string>
<string name="label_color" msgid="1108690305218188969">"Colour"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="6300874667546617333">"Pages (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +63,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Couldn\'t generate print job"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Colour"</item>
@@ -76,4 +75,9 @@
<item msgid="7421377442011699994">"All"</item>
<item msgid="6812869625222503603">"Range"</item>
</string-array>
+ <string name="print_write_error_message" msgid="5787642615179572543">"Couldn\'t write to file"</string>
+ <string name="print_error_default_message" msgid="8568506918983980567">"Couldn\'t generate print job"</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"Retry"</string>
+ <string name="print_error_printer_unavailable" msgid="6653128543854282851">"Printer unavailable"</string>
+ <string name="print_operation_canceling" msgid="5274571823242489160">"Cancelling…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-en-rIN/strings.xml b/packages/PrintSpooler/res/values-en-rIN/strings.xml
index 27372f8..3728437 100644
--- a/packages/PrintSpooler/res/values-en-rIN/strings.xml
+++ b/packages/PrintSpooler/res/values-en-rIN/strings.xml
@@ -17,12 +17,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Printer settings"</string>
- <string name="print_button" msgid="645164566271246268">"Print"</string>
- <string name="save_button" msgid="1921310454071758999">"Save"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"More options"</string>
<string name="label_destination" msgid="9132510997381599275">"Destination"</string>
<string name="label_copies" msgid="3634531042822968308">"Copies"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Paper Size"</string>
+ <string name="label_copies_summary" msgid="3861966063536529540">"Copies:"</string>
+ <string name="label_paper_size" msgid="908654383827777759">"Paper size"</string>
+ <string name="label_paper_size_summary" msgid="5668204981332138168">"Paper size:"</string>
<string name="label_color" msgid="1108690305218188969">"Colour"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="6300874667546617333">"Pages (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +63,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No connection to printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"unknown"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – unavailable"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Couldn\'t generate print job"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Colour"</item>
@@ -76,4 +75,9 @@
<item msgid="7421377442011699994">"All"</item>
<item msgid="6812869625222503603">"Range"</item>
</string-array>
+ <string name="print_write_error_message" msgid="5787642615179572543">"Couldn\'t write to file"</string>
+ <string name="print_error_default_message" msgid="8568506918983980567">"Couldn\'t generate print job"</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"Retry"</string>
+ <string name="print_error_printer_unavailable" msgid="6653128543854282851">"Printer unavailable"</string>
+ <string name="print_operation_canceling" msgid="5274571823242489160">"Cancelling…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-es-rUS/strings.xml b/packages/PrintSpooler/res/values-es-rUS/strings.xml
index c7d12af..8050ea5 100644
--- a/packages/PrintSpooler/res/values-es-rUS/strings.xml
+++ b/packages/PrintSpooler/res/values-es-rUS/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Cola de impresión"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Config. de impresora"</string>
- <string name="print_button" msgid="645164566271246268">"Imprimir"</string>
- <string name="save_button" msgid="1921310454071758999">"Guardar"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destino"</string>
<string name="label_copies" msgid="3634531042822968308">"Copias"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Tamaño del papel"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Color"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
<string name="label_pages" msgid="6300874667546617333">"Páginas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora."</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconocido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>: no disponible"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Error al generar el trabajo de impresión"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Blanco y negro"</item>
<item msgid="2762241247228983754">"Color"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Todas"</item>
<item msgid="6812869625222503603">"Intervalo"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Error al generar el trabajo de impresión"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-es/strings.xml b/packages/PrintSpooler/res/values-es/strings.xml
index 0225cad..f8bf6a4 100644
--- a/packages/PrintSpooler/res/values-es/strings.xml
+++ b/packages/PrintSpooler/res/values-es/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Cola de impresión"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Ajustes de impresora"</string>
- <string name="print_button" msgid="645164566271246268">"Imprimir"</string>
- <string name="save_button" msgid="1921310454071758999">"Guardar"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destino"</string>
<string name="label_copies" msgid="3634531042822968308">"Copias"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Tamaño del papel"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Color"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientación"</string>
<string name="label_pages" msgid="6300874667546617333">"Páginas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"No hay conexión con la impresora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconocido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – no disponible"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Error al generar el trabajo de impresión"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Blanco y negro"</item>
<item msgid="2762241247228983754">"Color"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Todo"</item>
<item msgid="6812869625222503603">"Intervalo"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Error al generar el trabajo de impresión"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-et-rEE/strings.xml b/packages/PrintSpooler/res/values-et-rEE/strings.xml
index 2b3b352..301be4c 100644
--- a/packages/PrintSpooler/res/values-et-rEE/strings.xml
+++ b/packages/PrintSpooler/res/values-et-rEE/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Prindispuuler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Printeri seaded"</string>
- <string name="print_button" msgid="645164566271246268">"Prindi"</string>
- <string name="save_button" msgid="1921310454071758999">"Salvesta"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Sihtkoht"</string>
<string name="label_copies" msgid="3634531042822968308">"Koopiaid"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Paberiformaat"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Värv"</string>
<string name="label_orientation" msgid="2853142581990496477">"Suund"</string>
<string name="label_pages" msgid="6300874667546617333">"Lehti (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Printeriühendus puudub"</string>
<string name="reason_unknown" msgid="5507940196503246139">"teadmata"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – pole saadaval"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Prinditööd ei saanud luua"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Mustvalge"</item>
<item msgid="2762241247228983754">"Värv"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Kõik"</item>
<item msgid="6812869625222503603">"Vahemik"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Prinditööd ei saanud luua"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 49bae323..9aa0aeb 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -17,12 +17,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"هماهنگکننده چاپ"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"تنظیمات چاپگر"</string>
- <string name="print_button" msgid="645164566271246268">"چاپ"</string>
- <string name="save_button" msgid="1921310454071758999">"ذخیره"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"گزینههای بیشتر"</string>
<string name="label_destination" msgid="9132510997381599275">"مقصد"</string>
<string name="label_copies" msgid="3634531042822968308">"کپیها"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"اندازه کاغذ"</string>
+ <string name="label_copies_summary" msgid="3861966063536529540">"تعداد نسخهها:"</string>
+ <string name="label_paper_size" msgid="908654383827777759">"اندازه کاغذ"</string>
+ <string name="label_paper_size_summary" msgid="5668204981332138168">"اندازه کاغذ:"</string>
<string name="label_color" msgid="1108690305218188969">"رنگی"</string>
<string name="label_orientation" msgid="2853142581990496477">"جهت"</string>
<string name="label_pages" msgid="6300874667546617333">"صفحات (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +63,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"اتصال با چاپگر برقرار نیست"</string>
<string name="reason_unknown" msgid="5507940196503246139">"نامعلوم"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - در دسترس نیست"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"کار چاپ ایجاد نشد"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"سیاه و سفید"</item>
<item msgid="2762241247228983754">"رنگی"</item>
@@ -76,4 +75,9 @@
<item msgid="7421377442011699994">"همه"</item>
<item msgid="6812869625222503603">"محدوده"</item>
</string-array>
+ <string name="print_write_error_message" msgid="5787642615179572543">"در فایل نوشته نشد"</string>
+ <string name="print_error_default_message" msgid="8568506918983980567">"کار چاپ ایجاد نشد"</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"امتحان مجدد"</string>
+ <string name="print_error_printer_unavailable" msgid="6653128543854282851">"چاپگر در دسترس نیست"</string>
+ <string name="print_operation_canceling" msgid="5274571823242489160">"در حال لغو…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-fi/strings.xml b/packages/PrintSpooler/res/values-fi/strings.xml
index 8658e04..8dcaa9c 100644
--- a/packages/PrintSpooler/res/values-fi/strings.xml
+++ b/packages/PrintSpooler/res/values-fi/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Taustatulostus"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Tulostimen asetukset"</string>
- <string name="print_button" msgid="645164566271246268">"Tulosta"</string>
- <string name="save_button" msgid="1921310454071758999">"Tallenna"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Kohde"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopiot"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Paperikoko"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Väri"</string>
<string name="label_orientation" msgid="2853142581990496477">"Suunta"</string>
<string name="label_pages" msgid="6300874667546617333">"Sivut (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ei yhteyttä tulostimeen"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tuntematon"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ei käytettävissä"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Tulostustyötä ei voitu luoda"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Mustavalkoinen"</item>
<item msgid="2762241247228983754">"Väri"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Kaikki"</item>
<item msgid="6812869625222503603">"Väli"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Tulostustyötä ei voitu luoda"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-fr-rCA/strings.xml b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
index 9a3352c..b37c8ac 100644
--- a/packages/PrintSpooler/res/values-fr-rCA/strings.xml
+++ b/packages/PrintSpooler/res/values-fr-rCA/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"File d\'att. impr."</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Paramètres de l\'imprimante"</string>
- <string name="print_button" msgid="645164566271246268">"Imprimer"</string>
- <string name="save_button" msgid="1921310454071758999">"Enregistrer"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destination"</string>
<string name="label_copies" msgid="3634531042822968308">"Copies"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Format du papier"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Couleur"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="6300874667546617333">"Pages (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante"</string>
<string name="reason_unknown" msgid="5507940196503246139">"inconnu"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> — indisponible"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Impossible de générer la tâche d\'impression"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Noir et blanc"</item>
<item msgid="2762241247228983754">"Couleur"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Tous"</item>
<item msgid="6812869625222503603">"Plage"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Impossible de générer la tâche d\'impression"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index 17fabdc..971039b 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Spouler impress."</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Paramètres de l\'imprimante"</string>
- <string name="print_button" msgid="645164566271246268">"Imprimer"</string>
- <string name="save_button" msgid="1921310454071758999">"Enregistrer"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destination"</string>
<string name="label_copies" msgid="3634531042822968308">"Copies"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Format du papier"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Couleur"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientation"</string>
<string name="label_pages" msgid="6300874667546617333">"Pages (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Aucune connexion à l\'imprimante."</string>
<string name="reason_unknown" msgid="5507940196503246139">"inconnue"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – indisponible"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Impossible de générer la tâche d\'impression."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Noir et blanc"</item>
<item msgid="2762241247228983754">"Couleur"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Tout"</item>
<item msgid="6812869625222503603">"Plage"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Impossible de générer la tâche d\'impression."</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index 60406b7..c46dff8 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"प्रिंट स्पूलर"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"प्रिंटर सेटिंग"</string>
- <string name="print_button" msgid="645164566271246268">"प्रिंट करें"</string>
- <string name="save_button" msgid="1921310454071758999">"सहेजें"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"गंतव्य"</string>
<string name="label_copies" msgid="3634531042822968308">"प्रतियां"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"कागज़ का आकार"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"रंग"</string>
<string name="label_orientation" msgid="2853142581990496477">"अभिविन्यास"</string>
<string name="label_pages" msgid="6300874667546617333">"पृष्ठ (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटर के लिए कोई कनेक्शन नहीं"</string>
<string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – अनुपलब्ध"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"प्रिंट कार्य जनरेट नहीं किया जा सका"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"श्याम और श्वेत"</item>
<item msgid="2762241247228983754">"रंग"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"सभी"</item>
<item msgid="6812869625222503603">"सीमा"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"प्रिंट कार्य जनरेट नहीं किया जा सका"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-hr/strings.xml b/packages/PrintSpooler/res/values-hr/strings.xml
index 7182cf6..51e750a 100644
--- a/packages/PrintSpooler/res/values-hr/strings.xml
+++ b/packages/PrintSpooler/res/values-hr/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Postavke pisača"</string>
- <string name="print_button" msgid="645164566271246268">"Ispis"</string>
- <string name="save_button" msgid="1921310454071758999">"Spremi"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Odredište"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopije"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Veličina papira"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"U boji"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orijentacija"</string>
<string name="label_pages" msgid="6300874667546617333">"Stranice (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nema veze s pisačem"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nepoznato"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – zadatak nije dostupan"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Zadatak ispisa nije generiran"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Crno-bijelo"</item>
<item msgid="2762241247228983754">"U boji"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Sve"</item>
<item msgid="6812869625222503603">"Raspon"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Zadatak ispisa nije generiran"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-hu/strings.xml b/packages/PrintSpooler/res/values-hu/strings.xml
index 6a0741b..fb193f0 100644
--- a/packages/PrintSpooler/res/values-hu/strings.xml
+++ b/packages/PrintSpooler/res/values-hu/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Nyomtatásisor-kezelő"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Nyomtatóbeállítások"</string>
- <string name="print_button" msgid="645164566271246268">"Nyomtatás"</string>
- <string name="save_button" msgid="1921310454071758999">"Mentés"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Cél"</string>
<string name="label_copies" msgid="3634531042822968308">"Példányszám"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Papírméret"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Szín"</string>
<string name="label_orientation" msgid="2853142581990496477">"Tájolás"</string>
<string name="label_pages" msgid="6300874667546617333">"Oldalszám (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nincs kapcsolat a nyomtatóval"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ismeretlen"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nem érhető el"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Nem sikerült létrehozni a nyomtatási feladatot."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Fekete-fehér"</item>
<item msgid="2762241247228983754">"Szín"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Összes"</item>
<item msgid="6812869625222503603">"Tartomány"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Nem sikerült létrehozni a nyomtatási feladatot."</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-hy-rAM/strings.xml b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
index 1423b82..1f1891b 100644
--- a/packages/PrintSpooler/res/values-hy-rAM/strings.xml
+++ b/packages/PrintSpooler/res/values-hy-rAM/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Տպման կարգավար"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Տպիչի կարգավորումներ"</string>
- <string name="print_button" msgid="645164566271246268">"Տպել"</string>
- <string name="save_button" msgid="1921310454071758999">"Պահել"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Նպատակակետ"</string>
<string name="label_copies" msgid="3634531042822968308">"Պատճեններ"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Թղթի չափը"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Գույնը"</string>
<string name="label_orientation" msgid="2853142581990496477">"Դիրքավորում"</string>
<string name="label_pages" msgid="6300874667546617333">"Էջեր (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Տպիչի հետ կապ չկա"</string>
<string name="reason_unknown" msgid="5507940196503246139">"անհայտ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> տպիչն անհասանելի է"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Չկարողացանք մշակել տպման աշխատանքը"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Սև ու սպիտակ"</item>
<item msgid="2762241247228983754">"Գույնը"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Բոլորը"</item>
<item msgid="6812869625222503603">"Միջակայք"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Չկարողացանք մշակել տպման աշխատանքը"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-in/strings.xml b/packages/PrintSpooler/res/values-in/strings.xml
index 2b80d07..d5bc1f6 100644
--- a/packages/PrintSpooler/res/values-in/strings.xml
+++ b/packages/PrintSpooler/res/values-in/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Setelan printer"</string>
- <string name="print_button" msgid="645164566271246268">"Cetak"</string>
- <string name="save_button" msgid="1921310454071758999">"Simpan"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Tujuan"</string>
<string name="label_copies" msgid="3634531042822968308">"Salinan"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Ukuran Kertas"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Warna"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientasi"</string>
<string name="label_pages" msgid="6300874667546617333">"(<xliff:g id="PAGE_COUNT">%1$s</xliff:g>) halaman"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Tidak ada sambungan ke printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tak diketahui"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Tidak dapat membuat tugas cetak"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Hitam & Putih"</item>
<item msgid="2762241247228983754">"Warna"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Semua"</item>
<item msgid="6812869625222503603">"Rentang"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Tidak dapat membuat tugas cetak"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-it/strings.xml b/packages/PrintSpooler/res/values-it/strings.xml
index 96c3b48..0f9201e 100644
--- a/packages/PrintSpooler/res/values-it/strings.xml
+++ b/packages/PrintSpooler/res/values-it/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Impostazioni stampante"</string>
- <string name="print_button" msgid="645164566271246268">"Stampa"</string>
- <string name="save_button" msgid="1921310454071758999">"Salva"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destinazione"</string>
<string name="label_copies" msgid="3634531042822968308">"Copie"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Formato carta"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"A colori"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientamento"</string>
<string name="label_pages" msgid="6300874667546617333">"Pagine (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nessun collegamento alla stampante"</string>
<string name="reason_unknown" msgid="5507940196503246139">"sconosciuto"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - non disponibile"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Impossibile generare processo di stampa"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Bianco e nero"</item>
<item msgid="2762241247228983754">"A colori"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Tutte"</item>
<item msgid="6812869625222503603">"Intervallo"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Impossibile generare processo di stampa"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-iw/strings.xml b/packages/PrintSpooler/res/values-iw/strings.xml
index 3e0e732..e69221a 100644
--- a/packages/PrintSpooler/res/values-iw/strings.xml
+++ b/packages/PrintSpooler/res/values-iw/strings.xml
@@ -17,12 +17,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"הגדרות מדפסת"</string>
- <string name="print_button" msgid="645164566271246268">"הדפס"</string>
- <string name="save_button" msgid="1921310454071758999">"שמור"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"עוד אפשרויות"</string>
<string name="label_destination" msgid="9132510997381599275">"יעד"</string>
<string name="label_copies" msgid="3634531042822968308">"עותקים"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"גודל נייר"</string>
+ <string name="label_copies_summary" msgid="3861966063536529540">"עותקים:"</string>
+ <string name="label_paper_size" msgid="908654383827777759">"גודל נייר"</string>
+ <string name="label_paper_size_summary" msgid="5668204981332138168">"גודל נייר:"</string>
<string name="label_color" msgid="1108690305218188969">"צבע"</string>
<string name="label_orientation" msgid="2853142581990496477">"כיוון"</string>
<string name="label_pages" msgid="6300874667546617333">"עמודים (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +63,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"אין חיבור למדפסת"</string>
<string name="reason_unknown" msgid="5507940196503246139">"לא ידוע"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – לא זמינה"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"לא ניתן היה ליצור את עבודת ההדפסה"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"שחור ולבן"</item>
<item msgid="2762241247228983754">"צבע"</item>
@@ -76,4 +75,9 @@
<item msgid="7421377442011699994">"הכל"</item>
<item msgid="6812869625222503603">"טווח"</item>
</string-array>
+ <string name="print_write_error_message" msgid="5787642615179572543">"לא ניתן היה לכתוב לקובץ"</string>
+ <string name="print_error_default_message" msgid="8568506918983980567">"לא ניתן היה ליצור את עבודת ההדפסה"</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"נסה שוב"</string>
+ <string name="print_error_printer_unavailable" msgid="6653128543854282851">"המדפסת אינה זמינה"</string>
+ <string name="print_operation_canceling" msgid="5274571823242489160">"מבטל…"</string>
</resources>
diff --git a/packages/PrintSpooler/res/values-ja/strings.xml b/packages/PrintSpooler/res/values-ja/strings.xml
index d3f4f85..f01a157 100644
--- a/packages/PrintSpooler/res/values-ja/strings.xml
+++ b/packages/PrintSpooler/res/values-ja/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"印刷スプーラ"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"プリンタの設定"</string>
- <string name="print_button" msgid="645164566271246268">"印刷"</string>
- <string name="save_button" msgid="1921310454071758999">"保存"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"印刷先"</string>
<string name="label_copies" msgid="3634531042822968308">"部数"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"用紙サイズ"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"カラー選択"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
<string name="label_pages" msgid="6300874667546617333">"ページ(<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"プリンタに接続されていません"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>–使用不可"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"印刷ジョブを生成できませんでした"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"モノクロ"</item>
<item msgid="2762241247228983754">"カラー"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"すべて"</item>
<item msgid="6812869625222503603">"範囲"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"印刷ジョブを生成できませんでした"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-ka-rGE/strings.xml b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
index d36b7c9..c645a7d 100644
--- a/packages/PrintSpooler/res/values-ka-rGE/strings.xml
+++ b/packages/PrintSpooler/res/values-ka-rGE/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"ბეჭდვის Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"პრინტერის პარამეტრები"</string>
- <string name="print_button" msgid="645164566271246268">"ბეჭდვა"</string>
- <string name="save_button" msgid="1921310454071758999">"შენახვა"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"დანიშნულება"</string>
<string name="label_copies" msgid="3634531042822968308">"ასლები"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"ფურცლის ზომა"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"ფერი"</string>
<string name="label_orientation" msgid="2853142581990496477">"ორიენტაცია"</string>
<string name="label_pages" msgid="6300874667546617333">"გვერდები (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"პრინტერთან კავშირი არ არის"</string>
<string name="reason_unknown" msgid="5507940196503246139">"უცნობი"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – მიუწვდომელია"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"ბეჭდვის დავალების გენერაცია ვერ ხერხდება"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"შავ-თეთრი"</item>
<item msgid="2762241247228983754">"ფერი"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"ყველა"</item>
<item msgid="6812869625222503603">"დიაპაზონი"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"ბეჭდვის დავალების გენერაცია ვერ ხერხდება"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index c89f9bf..a456314 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"បោះពុម្ពស្ពូល័រ"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"ការកំណត់ម៉ាស៊ីនបោះពុម្ព"</string>
- <string name="print_button" msgid="645164566271246268">"បោះពុម្ព"</string>
- <string name="save_button" msgid="1921310454071758999">"រក្សាទុក"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"ទិសដៅ"</string>
<string name="label_copies" msgid="3634531042822968308">"ច្បាប់ចម្លង"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"ទំហំក្រដាស"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"ពណ៌"</string>
<string name="label_orientation" msgid="2853142581990496477">"ទិស"</string>
<string name="label_pages" msgid="6300874667546617333">"ទំព័រ (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -60,10 +64,9 @@
</plurals>
<string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
<string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើមឡើងវិញ"</string>
- <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
+ <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មានការភ្ជាប់ទៅម៉ាស៊ីនបោះពុម្ព"</string>
<string name="reason_unknown" msgid="5507940196503246139">"មិនស្គាល់"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិនអាចប្រើបាន"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"មិនអាចបង្កើតការងារបោះពុម្ព"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ស & ខ្មៅ"</item>
<item msgid="2762241247228983754">"ពណ៌"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"ទាំងអស់"</item>
<item msgid="6812869625222503603">"ជួរ"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"មិនអាចបង្កើតការងារបោះពុម្ព"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-ko/strings.xml b/packages/PrintSpooler/res/values-ko/strings.xml
index 3b2fef7..c1ace69 100644
--- a/packages/PrintSpooler/res/values-ko/strings.xml
+++ b/packages/PrintSpooler/res/values-ko/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"인쇄 스풀러"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"프린터 설정"</string>
- <string name="print_button" msgid="645164566271246268">"인쇄"</string>
- <string name="save_button" msgid="1921310454071758999">"저장"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"대상"</string>
<string name="label_copies" msgid="3634531042822968308">"매수"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"용지 크기"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"색상"</string>
<string name="label_orientation" msgid="2853142581990496477">"방향"</string>
<string name="label_pages" msgid="6300874667546617333">"페이지 수(<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"프린터와 연결되지 않음"</string>
<string name="reason_unknown" msgid="5507940196503246139">"알 수 없음"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 사용할 수 없음"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"인쇄 작업을 생성할 수 없습니다."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"흑백"</item>
<item msgid="2762241247228983754">"컬러"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"모두"</item>
<item msgid="6812869625222503603">"범위"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"인쇄 작업을 생성할 수 없습니다."</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-land/constants.xml b/packages/PrintSpooler/res/values-land/constants.xml
index d68b77e..0db7513 100644
--- a/packages/PrintSpooler/res/values-land/constants.xml
+++ b/packages/PrintSpooler/res/values-land/constants.xml
@@ -18,5 +18,6 @@
<dimen name="printer_list_view_padding_start">48dip</dimen>
<dimen name="printer_list_view_padding_end">48dip</dimen>
+ <integer name="print_option_column_count">3</integer>
</resources>
diff --git a/packages/PrintSpooler/res/values-lo-rLA/strings.xml b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
index f954606..c8c6caa 100644
--- a/packages/PrintSpooler/res/values-lo-rLA/strings.xml
+++ b/packages/PrintSpooler/res/values-lo-rLA/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"ຕົວຈັດຄິວການພິມ"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"ການຕັ້ງຄ່າເຄື່ອງພິມ"</string>
- <string name="print_button" msgid="645164566271246268">"ພິມ"</string>
- <string name="save_button" msgid="1921310454071758999">"ບັນທຶກ"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"ປາຍທາງ"</string>
<string name="label_copies" msgid="3634531042822968308">"ສຳເນົາ"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"ຂະໜາດຂອງໜ້າເຈ້ຍ"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"ສີ"</string>
<string name="label_orientation" msgid="2853142581990496477">"ລວງ"</string>
<string name="label_pages" msgid="6300874667546617333">"(<xliff:g id="PAGE_COUNT">%1$s</xliff:g>) ໜ້າ"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ບໍ່ມີການເຊື່ອມຕໍ່ຫາເຄື່ອງພິມ"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ບໍ່ຮູ້ຈັກ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - ບໍ່ມີຢູ່"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"ບໍ່ສາມາດສ້າງວຽກພິມໄດ້"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ຂາວດຳ"</item>
<item msgid="2762241247228983754">"ສີ"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"ທັງໝົດ"</item>
<item msgid="6812869625222503603">"ໄລຍະ"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"ບໍ່ສາມາດສ້າງວຽກພິມໄດ້"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-lt/strings.xml b/packages/PrintSpooler/res/values-lt/strings.xml
index 0c4e386..0fff75a 100644
--- a/packages/PrintSpooler/res/values-lt/strings.xml
+++ b/packages/PrintSpooler/res/values-lt/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Spausdintuvo nustatymai"</string>
- <string name="print_button" msgid="645164566271246268">"Spausdinti"</string>
- <string name="save_button" msgid="1921310454071758999">"Išsaugoti"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Paskirties vieta"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopijos"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Popieriaus dydis"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Spalva"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientacija"</string>
<string name="label_pages" msgid="6300874667546617333">"Puslapiai (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nėra ryšio su spausdintuvu"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nežinoma"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"„<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>“ – nepasiekiama"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Nepavyko sukurti spausdinimo užduoties"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Nespalvotas"</item>
<item msgid="2762241247228983754">"Spalva"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Visi"</item>
<item msgid="6812869625222503603">"Diapazonas"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Nepavyko sukurti spausdinimo užduoties"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-lv/strings.xml b/packages/PrintSpooler/res/values-lv/strings.xml
index 3fffdfe..55ae463 100644
--- a/packages/PrintSpooler/res/values-lv/strings.xml
+++ b/packages/PrintSpooler/res/values-lv/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Printera iestatījumi"</string>
- <string name="print_button" msgid="645164566271246268">"Drukāt"</string>
- <string name="save_button" msgid="1921310454071758999">"Saglabāt"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Galamērķis"</string>
<string name="label_copies" msgid="3634531042822968308">"Eksemplāri"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Papīra izmērs"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Krāsa"</string>
<string name="label_orientation" msgid="2853142581990496477">"Virziens"</string>
<string name="label_pages" msgid="6300874667546617333">"Lapas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nav savienojuma ar printeri"</string>
<string name="reason_unknown" msgid="5507940196503246139">"nezināms"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> — nav pieejams"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Nevarēja ģenerēt drukas darbu"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Melnbalts"</item>
<item msgid="2762241247228983754">"Krāsa"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Visi"</item>
<item msgid="6812869625222503603">"Diapazons"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Nevarēja ģenerēt drukas darbu"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-mn-rMN/strings.xml b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
index e429387..7eb9b06 100644
--- a/packages/PrintSpooler/res/values-mn-rMN/strings.xml
+++ b/packages/PrintSpooler/res/values-mn-rMN/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Хэвлэгчийн буфер"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Принтерийн тохиргоо"</string>
- <string name="print_button" msgid="645164566271246268">"Хэвлэх"</string>
- <string name="save_button" msgid="1921310454071758999">"Хадгалах"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Хүлээн авагч"</string>
<string name="label_copies" msgid="3634531042822968308">"Хуулбарууд"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Цаасны хэмжээ"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Өнгө"</string>
<string name="label_orientation" msgid="2853142581990496477">"Чиглэл"</string>
<string name="label_pages" msgid="6300874667546617333">"(<xliff:g id="PAGE_COUNT">%1$s</xliff:g>) хуудас"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Принтер холбогдоогүй байна"</string>
<string name="reason_unknown" msgid="5507940196503246139">"тодорхойгүй"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ашиглах боломжгүй"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Хэвлэх ажлыг үүсгэж чадсангүй"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Хар & Цагаан"</item>
<item msgid="2762241247228983754">"Өнгө"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Бүгд"</item>
<item msgid="6812869625222503603">"Хүрээ"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Хэвлэх ажлыг үүсгэж чадсангүй"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-ms-rMY/strings.xml b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
index cca5d2c..fa4abc9 100644
--- a/packages/PrintSpooler/res/values-ms-rMY/strings.xml
+++ b/packages/PrintSpooler/res/values-ms-rMY/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Penspul Cetakan"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Tetapan pencetak"</string>
- <string name="print_button" msgid="645164566271246268">"Cetak"</string>
- <string name="save_button" msgid="1921310454071758999">"Simpan"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destinasi"</string>
<string name="label_copies" msgid="3634531042822968308">"Salinan"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Saiz Kertas"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Warna"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientasi"</string>
<string name="label_pages" msgid="6300874667546617333">"Halaman (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Tiada sambungan ke pencetak"</string>
<string name="reason_unknown" msgid="5507940196503246139">"tidak diketahui"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – tidak tersedia"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Tidak dapat menjana kerja cetakan"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Hitam & Putih"</item>
<item msgid="2762241247228983754">"Warna"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Semua"</item>
<item msgid="6812869625222503603">"Julat"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Tidak dapat menjana kerja cetakan"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-nb/strings.xml b/packages/PrintSpooler/res/values-nb/strings.xml
index f6a60c6..03d5248 100644
--- a/packages/PrintSpooler/res/values-nb/strings.xml
+++ b/packages/PrintSpooler/res/values-nb/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Utskriftskø"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Skriverinnstillinger"</string>
- <string name="print_button" msgid="645164566271246268">"Skriv ut"</string>
- <string name="save_button" msgid="1921310454071758999">"Lagre"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destinasjon"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopier"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Papirstørrelse"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Farge"</string>
<string name="label_orientation" msgid="2853142581990496477">"Retning"</string>
<string name="label_pages" msgid="6300874667546617333">"Sider (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen forbindelse med skriveren"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ukjent"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – utilgjengelig"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Kunne ikke generere utskriftsjobben"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Svart og hvitt"</item>
<item msgid="2762241247228983754">"Farge"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Alle"</item>
<item msgid="6812869625222503603">"Område"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Kunne ikke generere utskriftsjobben"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-nl/strings.xml b/packages/PrintSpooler/res/values-nl/strings.xml
index dc12508..a2e5391 100644
--- a/packages/PrintSpooler/res/values-nl/strings.xml
+++ b/packages/PrintSpooler/res/values-nl/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Afdrukspooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Printerinstellingen"</string>
- <string name="print_button" msgid="645164566271246268">"Afdrukken"</string>
- <string name="save_button" msgid="1921310454071758999">"Opslaan"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Bestemming"</string>
<string name="label_copies" msgid="3634531042822968308">"Aantal exemplaren"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Papierformaat"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Kleur"</string>
<string name="label_orientation" msgid="2853142581990496477">"Stand"</string>
<string name="label_pages" msgid="6300874667546617333">"Pagina\'s (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Geen verbinding met printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"onbekend"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – niet beschikbaar"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Kan de afdruktaak niet genereren"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Zwart-wit"</item>
<item msgid="2762241247228983754">"Kleur"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Alle"</item>
<item msgid="6812869625222503603">"Bereik"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Kan de afdruktaak niet genereren"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-pl/strings.xml b/packages/PrintSpooler/res/values-pl/strings.xml
index 81acc76..1500060 100644
--- a/packages/PrintSpooler/res/values-pl/strings.xml
+++ b/packages/PrintSpooler/res/values-pl/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Bufor wydruku"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Ustawienia drukarki"</string>
- <string name="print_button" msgid="645164566271246268">"Drukuj"</string>
- <string name="save_button" msgid="1921310454071758999">"Zapisz"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Miejsce docelowe"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopie"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Rozmiar papieru"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Kolor"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientacja"</string>
<string name="label_pages" msgid="6300874667546617333">"Strony (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Brak połączenia z drukarką"</string>
<string name="reason_unknown" msgid="5507940196503246139">"brak informacji"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – niedostępne"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Nie udało się wygenerować zadania drukowania"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Czarno-białe"</item>
<item msgid="2762241247228983754">"Kolor"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Wszystkie"</item>
<item msgid="6812869625222503603">"Zakres"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Nie udało się wygenerować zadania drukowania"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-pt-rPT/strings.xml b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
index 6bfc395..9bcd0d6 100644
--- a/packages/PrintSpooler/res/values-pt-rPT/strings.xml
+++ b/packages/PrintSpooler/res/values-pt-rPT/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Definições da impressora"</string>
- <string name="print_button" msgid="645164566271246268">"Imprimir"</string>
- <string name="save_button" msgid="1921310454071758999">"Guardar"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destino"</string>
<string name="label_copies" msgid="3634531042822968308">"Cópias"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Tamanho do papel"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Cor"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientação"</string>
<string name="label_pages" msgid="6300874667546617333">"Páginas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem ligação à impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – indisponível"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Não foi possível gerar a tarefa de impressão"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Preto e branco"</item>
<item msgid="2762241247228983754">"Cor"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Todas"</item>
<item msgid="6812869625222503603">"Intervalo"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Não foi possível gerar a tarefa de impressão"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-pt/strings.xml b/packages/PrintSpooler/res/values-pt/strings.xml
index 82c157d..f10e82e 100644
--- a/packages/PrintSpooler/res/values-pt/strings.xml
+++ b/packages/PrintSpooler/res/values-pt/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Sp. de impressão"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Configur. da impressora"</string>
- <string name="print_button" msgid="645164566271246268">"Imprimir"</string>
- <string name="save_button" msgid="1921310454071758999">"Salvar"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destino"</string>
<string name="label_copies" msgid="3634531042822968308">"Cópias"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Tamanho do papel"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Cor"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientação"</string>
<string name="label_pages" msgid="6300874667546617333">"Páginas (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Sem conexão com a impressora"</string>
<string name="reason_unknown" msgid="5507940196503246139">"desconhecido"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – não disponível"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Não foi possível gerar o trabalho de impressão"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Preto e branco"</item>
<item msgid="2762241247228983754">"Cor"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Todas"</item>
<item msgid="6812869625222503603">"Intervalo"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Não foi possível gerar o trabalho de impressão"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index 79c3bdc..18394b0 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Derulator print."</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Setările imprimantei"</string>
- <string name="print_button" msgid="645164566271246268">"Printați"</string>
- <string name="save_button" msgid="1921310454071758999">"Salvați"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destinație"</string>
<string name="label_copies" msgid="3634531042822968308">"Copii"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Formatul hârtiei"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Color"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientare"</string>
<string name="label_pages" msgid="6300874667546617333">"Pagini (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nu există conexiune la o imprimantă"</string>
<string name="reason_unknown" msgid="5507940196503246139">"necunoscut"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - indisponibil"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Nu s-a putut genera sarcina de printare"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Alb-negru"</item>
<item msgid="2762241247228983754">"Color"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Toate"</item>
<item msgid="6812869625222503603">"Interval"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Nu s-a putut genera sarcina de printare"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-ru/strings.xml b/packages/PrintSpooler/res/values-ru/strings.xml
index a27f3c8..c474752 100644
--- a/packages/PrintSpooler/res/values-ru/strings.xml
+++ b/packages/PrintSpooler/res/values-ru/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Спулер печати"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Настройки принтера"</string>
- <string name="print_button" msgid="645164566271246268">"Печать"</string>
- <string name="save_button" msgid="1921310454071758999">"Сохранить"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Принтер"</string>
<string name="label_copies" msgid="3634531042822968308">"Копии"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Формат"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Печать"</string>
<string name="label_orientation" msgid="2853142581990496477">"Ориентация"</string>
<string name="label_pages" msgid="6300874667546617333">"СТРАНИЦЫ (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нет связи с принтером"</string>
<string name="reason_unknown" msgid="5507940196503246139">"неизвестно"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступен"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Не удалось отправить документ на печать."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Черно-белая"</item>
<item msgid="2762241247228983754">"Цветная"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Все"</item>
<item msgid="6812869625222503603">"Диапазон"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Не удалось отправить документ на печать."</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-sk/strings.xml b/packages/PrintSpooler/res/values-sk/strings.xml
index 39a022d..59e7f4c 100644
--- a/packages/PrintSpooler/res/values-sk/strings.xml
+++ b/packages/PrintSpooler/res/values-sk/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Zaraďovač tlače"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Nastavenia tlačiarne"</string>
- <string name="print_button" msgid="645164566271246268">"Tlačiť"</string>
- <string name="save_button" msgid="1921310454071758999">"Uložiť"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Cieľ"</string>
<string name="label_copies" msgid="3634531042822968308">"Kópie"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Veľkosť papiera"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Farba"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientácia"</string>
<string name="label_pages" msgid="6300874667546617333">"STRÁNKY (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Žiadne pripojenie k tlačiarni"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznáme"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – nie je k dispozícii"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Tlačovú úlohu nie je možné vytvoriť"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Čiernobiele"</item>
<item msgid="2762241247228983754">"Farba"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Všetky"</item>
<item msgid="6812869625222503603">"Rozsah"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Tlačovú úlohu nie je možné vytvoriť"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-sl/strings.xml b/packages/PrintSpooler/res/values-sl/strings.xml
index e299508..3d27d65 100644
--- a/packages/PrintSpooler/res/values-sl/strings.xml
+++ b/packages/PrintSpooler/res/values-sl/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Tisk. v ozadju"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Nastavitve tiskalnika"</string>
- <string name="print_button" msgid="645164566271246268">"Natisni"</string>
- <string name="save_button" msgid="1921310454071758999">"Shrani"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Cilj"</string>
<string name="label_copies" msgid="3634531042822968308">"Št. kopij"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Velikost papirja"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Barvno"</string>
<string name="label_orientation" msgid="2853142581990496477">"Postavitev"</string>
<string name="label_pages" msgid="6300874667546617333">"Št. strani (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ni povezave s tiskalnikom"</string>
<string name="reason_unknown" msgid="5507940196503246139">"neznano"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ni na voljo"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Tiskalnega opravila ni bilo mogoče ustvariti"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Črno-belo"</item>
<item msgid="2762241247228983754">"Barvno"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Vse"</item>
<item msgid="6812869625222503603">"Obseg"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Tiskalnega opravila ni bilo mogoče ustvariti"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-sr/strings.xml b/packages/PrintSpooler/res/values-sr/strings.xml
index cbab1a6..b3c13a0 100644
--- a/packages/PrintSpooler/res/values-sr/strings.xml
+++ b/packages/PrintSpooler/res/values-sr/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Штамп. из мемор."</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Подешавања штампача"</string>
- <string name="print_button" msgid="645164566271246268">"Штампај"</string>
- <string name="save_button" msgid="1921310454071758999">"Сачувај"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Одредиште"</string>
<string name="label_copies" msgid="3634531042822968308">"Копије"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Величина папира"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Боја"</string>
<string name="label_orientation" msgid="2853142581990496477">"Положај"</string>
<string name="label_pages" msgid="6300874667546617333">"Странице (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Нема везе са штампачем"</string>
<string name="reason_unknown" msgid="5507940196503246139">"непознато"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – недоступан"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Није могуће генерисати задатак за штампање"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Црно-бело"</item>
<item msgid="2762241247228983754">"Боја"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Све"</item>
<item msgid="6812869625222503603">"Опсег"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Није могуће генерисати задатак за штампање"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-sv/strings.xml b/packages/PrintSpooler/res/values-sv/strings.xml
index 2286fce..58dfa40 100644
--- a/packages/PrintSpooler/res/values-sv/strings.xml
+++ b/packages/PrintSpooler/res/values-sv/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Utskriftskö"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Skrivarinställningar"</string>
- <string name="print_button" msgid="645164566271246268">"Skriv ut"</string>
- <string name="save_button" msgid="1921310454071758999">"Spara"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Destination"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopior"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Pappersstorlek"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Färg"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientering"</string>
<string name="label_pages" msgid="6300874667546617333">"Sidor (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Ingen anslutning till skrivaren"</string>
<string name="reason_unknown" msgid="5507940196503246139">"okänt"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – inte tillgänglig"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Det gick inte att skapa utskriftsjobbet"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Svartvit"</item>
<item msgid="2762241247228983754">"Färg"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Alla"</item>
<item msgid="6812869625222503603">"Intervall"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Det gick inte att skapa utskriftsjobbet"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-sw/strings.xml b/packages/PrintSpooler/res/values-sw/strings.xml
index a84e9b3..0bb9790 100644
--- a/packages/PrintSpooler/res/values-sw/strings.xml
+++ b/packages/PrintSpooler/res/values-sw/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Programu ya kuandaa Printa kwa ajili ya Kuchapisha"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Mipangilio ya printa"</string>
- <string name="print_button" msgid="645164566271246268">"Chapisha"</string>
- <string name="save_button" msgid="1921310454071758999">"Hifadhi"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Itakapofika"</string>
<string name="label_copies" msgid="3634531042822968308">"Nakala"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Ukubwa wa Karatasi"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Rangi"</string>
<string name="label_orientation" msgid="2853142581990496477">"Mkao"</string>
<string name="label_pages" msgid="6300874667546617333">"Kurasa (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Hakuna muunganisho kwa printa"</string>
<string name="reason_unknown" msgid="5507940196503246139">"haijulikani"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - haipatikani"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Haikuweza kuleta kazi ya kuchapisha"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Nyeusi na Nyeupe"</item>
<item msgid="2762241247228983754">"Rangi"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Zote"</item>
<item msgid="6812869625222503603">"Masafa"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Haikuweza kuleta kazi ya kuchapisha"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-sw600dp-land/constants.xml b/packages/PrintSpooler/res/values-sw600dp-land/constants.xml
new file mode 100644
index 0000000..cacdf98
--- /dev/null
+++ b/packages/PrintSpooler/res/values-sw600dp-land/constants.xml
@@ -0,0 +1,21 @@
+<?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>
+
+ <integer name="print_option_column_count">6</integer>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-sw600dp/constants.xml b/packages/PrintSpooler/res/values-sw600dp/constants.xml
new file mode 100644
index 0000000..14c099c
--- /dev/null
+++ b/packages/PrintSpooler/res/values-sw600dp/constants.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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>
+
+ <integer name="print_option_column_count">3</integer>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values-th/strings.xml b/packages/PrintSpooler/res/values-th/strings.xml
index aa01d6f..91fc544 100644
--- a/packages/PrintSpooler/res/values-th/strings.xml
+++ b/packages/PrintSpooler/res/values-th/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"การตั้งค่าเครื่องพิมพ์"</string>
- <string name="print_button" msgid="645164566271246268">"พิมพ์"</string>
- <string name="save_button" msgid="1921310454071758999">"บันทึก"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"ปลายทาง"</string>
<string name="label_copies" msgid="3634531042822968308">"สำเนา"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"ขนาดของกระดาษ"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"สี"</string>
<string name="label_orientation" msgid="2853142581990496477">"การวางแนว"</string>
<string name="label_pages" msgid="6300874667546617333">"หน้า (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"ไม่มีการเชื่อมต่อไปยังเครื่องพิมพ์"</string>
<string name="reason_unknown" msgid="5507940196503246139">"ไม่ทราบ"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ไม่พร้อมใช้งาน"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"ไม่สามารถสร้างงานพิมพ์"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"ขาวดำ"</item>
<item msgid="2762241247228983754">"สี"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"ทั้งหมด"</item>
<item msgid="6812869625222503603">"ช่วง"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"ไม่สามารถสร้างงานพิมพ์"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-tl/strings.xml b/packages/PrintSpooler/res/values-tl/strings.xml
index a72c937..111fee4 100644
--- a/packages/PrintSpooler/res/values-tl/strings.xml
+++ b/packages/PrintSpooler/res/values-tl/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Mga setting ng printer"</string>
- <string name="print_button" msgid="645164566271246268">"I-print"</string>
- <string name="save_button" msgid="1921310454071758999">"I-save"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Patutunguhan"</string>
<string name="label_copies" msgid="3634531042822968308">"Mga Kopya"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Laki ng Papel"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Kulay"</string>
<string name="label_orientation" msgid="2853142581990496477">"Oryentasyon"</string>
<string name="label_pages" msgid="6300874667546617333">"Mga Pahina (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Hindi nakakonekta sa printer"</string>
<string name="reason_unknown" msgid="5507940196503246139">"hindi alam"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – hindi available"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Hindi mabuo ang pag-print"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Black & White"</item>
<item msgid="2762241247228983754">"Kulay"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Lahat"</item>
<item msgid="6812869625222503603">"Sakop"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Hindi mabuo ang pag-print"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-tr/strings.xml b/packages/PrintSpooler/res/values-tr/strings.xml
index c6e302d..63fa270 100644
--- a/packages/PrintSpooler/res/values-tr/strings.xml
+++ b/packages/PrintSpooler/res/values-tr/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Yazıcı ayarları"</string>
- <string name="print_button" msgid="645164566271246268">"Yazdır"</string>
- <string name="save_button" msgid="1921310454071758999">"Kaydet"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Hedef"</string>
<string name="label_copies" msgid="3634531042822968308">"Kopya sayısı"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Kağıt Boyutu"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Renkli"</string>
<string name="label_orientation" msgid="2853142581990496477">"Sayfa yönü"</string>
<string name="label_pages" msgid="6300874667546617333">"Sayfa (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Yazıcı bağlantısı yok"</string>
<string name="reason_unknown" msgid="5507940196503246139">"bilinmiyor"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – kullanılamıyor"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Yazdırma işi oluşturulamadı"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Siyah Beyaz"</item>
<item msgid="2762241247228983754">"Renkli"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Tümü"</item>
<item msgid="6812869625222503603">"Aralık"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Yazdırma işi oluşturulamadı"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-uk/strings.xml b/packages/PrintSpooler/res/values-uk/strings.xml
index 4f526d0..bb0ee2c 100644
--- a/packages/PrintSpooler/res/values-uk/strings.xml
+++ b/packages/PrintSpooler/res/values-uk/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Налаштування принтера"</string>
- <string name="print_button" msgid="645164566271246268">"Друк"</string>
- <string name="save_button" msgid="1921310454071758999">"Зберегти"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Місце признач-ня"</string>
<string name="label_copies" msgid="3634531042822968308">"Копії"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Розмір паперу"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Колір"</string>
<string name="label_orientation" msgid="2853142581990496477">"Орієнтація"</string>
<string name="label_pages" msgid="6300874667546617333">"Сторінки (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Немає з’єднання з принтером"</string>
<string name="reason_unknown" msgid="5507940196503246139">"невідомо"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"Завдання \"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>\" не доступне"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Не вдалося створити завдання друку"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Чорно-білий"</item>
<item msgid="2762241247228983754">"Колір"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Усі"</item>
<item msgid="6812869625222503603">"Діапазон"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Не вдалося створити завдання друку"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index d21172c..0c42ccc 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Cài đặt máy in"</string>
- <string name="print_button" msgid="645164566271246268">"In"</string>
- <string name="save_button" msgid="1921310454071758999">"Lưu"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Đích"</string>
<string name="label_copies" msgid="3634531042822968308">"Bản sao"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Kích thước trang"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Màu"</string>
<string name="label_orientation" msgid="2853142581990496477">"Hướng"</string>
<string name="label_pages" msgid="6300874667546617333">"Trang (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Không có kết nối nào với máy in"</string>
<string name="reason_unknown" msgid="5507940196503246139">"không xác định"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – không khả dụng"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Không thể tạo lệnh in"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Đen trắng"</item>
<item msgid="2762241247228983754">"Màu"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Tất cả"</item>
<item msgid="6812869625222503603">"Dãy"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Không thể tạo lệnh in"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rCN/strings.xml b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
index e43ea60..c853d54 100644
--- a/packages/PrintSpooler/res/values-zh-rCN/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rCN/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"打印处理服务"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"打印机设置"</string>
- <string name="print_button" msgid="645164566271246268">"打印"</string>
- <string name="save_button" msgid="1921310454071758999">"保存"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"目的地"</string>
<string name="label_copies" msgid="3634531042822968308">"份数"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"纸张尺寸"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"颜色"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
<string name="label_pages" msgid="6300874667546617333">"页数 (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"未与打印机建立连接"</string>
<string name="reason_unknown" msgid="5507940196503246139">"未知"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> - 无法使用"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"无法生成打印作业"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"全部"</item>
<item msgid="6812869625222503603">"范围"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"无法生成打印作业"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rHK/strings.xml b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
index 2c2422a9..5d75382 100644
--- a/packages/PrintSpooler/res/values-zh-rHK/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rHK/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"列印多工緩衝處理器"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"打印機設定"</string>
- <string name="print_button" msgid="645164566271246268">"列印"</string>
- <string name="save_button" msgid="1921310454071758999">"儲存"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"目的地"</string>
<string name="label_copies" msgid="3634531042822968308">"份數"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"紙張大小"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"顏色"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
<string name="label_pages" msgid="6300874667546617333">"頁數 (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與打印機連線"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 無法使用"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"無法產生列印工作"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"全部"</item>
<item msgid="6812869625222503603">"範圍"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"無法產生列印工作"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-zh-rTW/strings.xml b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
index 0fe88d9..a2e1637 100644
--- a/packages/PrintSpooler/res/values-zh-rTW/strings.xml
+++ b/packages/PrintSpooler/res/values-zh-rTW/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"列印多工緩衝處理器"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"印表機設定"</string>
- <string name="print_button" msgid="645164566271246268">"列印"</string>
- <string name="save_button" msgid="1921310454071758999">"儲存"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"目的地"</string>
<string name="label_copies" msgid="3634531042822968308">"份數"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"紙張大小"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"色彩"</string>
<string name="label_orientation" msgid="2853142581990496477">"方向"</string>
<string name="label_pages" msgid="6300874667546617333">"頁數 (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"尚未與印表機建立連線"</string>
<string name="reason_unknown" msgid="5507940196503246139">"不明"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – 無法使用"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"無法產生列印工作"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"黑白"</item>
<item msgid="2762241247228983754">"彩色"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"全部"</item>
<item msgid="6812869625222503603">"範圍"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"無法產生列印工作"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values-zu/strings.xml b/packages/PrintSpooler/res/values-zu/strings.xml
index 08e31e1..2837949 100644
--- a/packages/PrintSpooler/res/values-zu/strings.xml
+++ b/packages/PrintSpooler/res/values-zu/strings.xml
@@ -17,12 +17,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Ispuli sephrinta"</string>
- <string name="advanced_settings_button" msgid="5764225091289415632">"Izilungiselelo zephrinta"</string>
- <string name="print_button" msgid="645164566271246268">"Phrinta"</string>
- <string name="save_button" msgid="1921310454071758999">"Londoloza"</string>
+ <!-- no translation found for more_options_button (2243228396432556771) -->
+ <skip />
<string name="label_destination" msgid="9132510997381599275">"Indawo"</string>
<string name="label_copies" msgid="3634531042822968308">"Amakhophi"</string>
- <string name="label_paper_size" msgid="8681895607876809323">"Usayizi wephepha"</string>
+ <!-- no translation found for label_copies_summary (3861966063536529540) -->
+ <skip />
+ <!-- no translation found for label_paper_size (908654383827777759) -->
+ <skip />
+ <!-- no translation found for label_paper_size_summary (5668204981332138168) -->
+ <skip />
<string name="label_color" msgid="1108690305218188969">"Umbala"</string>
<string name="label_orientation" msgid="2853142581990496477">"Umumo"</string>
<string name="label_pages" msgid="6300874667546617333">"Amakhasi (<xliff:g id="PAGE_COUNT">%1$s</xliff:g>)"</string>
@@ -63,7 +67,6 @@
<string name="no_connection_to_printer" msgid="2159246915977282728">"Akukho ukuxhumana kuphrinta"</string>
<string name="reason_unknown" msgid="5507940196503246139">"akwaziwa"</string>
<string name="printer_unavailable" msgid="2434170617003315690">"I-<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – ayitholakali"</string>
- <string name="print_error_default_message" msgid="8568506918983980567">"Ayikwazanga ukukhiqiza umsebenzi wokuphrinta"</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Okumnyama nokumhlophe"</item>
<item msgid="2762241247228983754">"Umbala"</item>
@@ -76,4 +79,13 @@
<item msgid="7421377442011699994">"Konke"</item>
<item msgid="6812869625222503603">"Ibanga"</item>
</string-array>
+ <!-- no translation found for print_write_error_message (5787642615179572543) -->
+ <skip />
+ <string name="print_error_default_message" msgid="8568506918983980567">"Ayikwazanga ukukhiqiza umsebenzi wokuphrinta"</string>
+ <!-- no translation found for print_error_retry (1426421728784259538) -->
+ <skip />
+ <!-- no translation found for print_error_printer_unavailable (6653128543854282851) -->
+ <skip />
+ <!-- no translation found for print_operation_canceling (5274571823242489160) -->
+ <skip />
</resources>
diff --git a/packages/PrintSpooler/res/values/attrs.xml b/packages/PrintSpooler/res/values/attrs.xml
new file mode 100644
index 0000000..feb8be1
--- /dev/null
+++ b/packages/PrintSpooler/res/values/attrs.xml
@@ -0,0 +1,25 @@
+<?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>
+
+ <declare-styleable name="PrintOptionsLayout">
+
+ <attr name="columnCount" format="integer" />
+
+ </declare-styleable>
+
+</resources>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index 4fc25b3..fd6ae195 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -16,10 +16,6 @@
<resources>
- <color name="container_background">#F2F2F2</color>
- <color name="important_text">#333333</color>
- <color name="print_option_title">#888888</color>
- <color name="separator">#CCCCCC</color>
- <color name="action_button_background">#FFFFFF</color>
+ <color name="print_button_tint_color">#EEFF41</color>
</resources>
diff --git a/packages/PrintSpooler/res/values/constants.xml b/packages/PrintSpooler/res/values/constants.xml
index e9c925c..9a2c14e 100644
--- a/packages/PrintSpooler/res/values/constants.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -19,6 +19,8 @@
<integer name="page_option_value_all">0</integer>
<integer name="page_option_value_page_range">1</integer>
+ <integer name="print_option_column_count">2</integer>
+
<integer-array name="page_options_values" translatable="false">
<item>@integer/page_option_value_all</item>
<item>@integer/page_option_value_page_range</item>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index d2613d0..d85529c 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -19,14 +19,8 @@
<!-- Title of the PrintSpooler application. [CHAR LIMIT=50] -->
<string name="app_label">Print Spooler</string>
- <!-- Label of the print dialog's button for advanced printer settings. [CHAR LIMIT=25] -->
- <string name="advanced_settings_button">Printer settings</string>
-
- <!-- Label of the print dialog's print button. [CHAR LIMIT=16] -->
- <string name="print_button">Print</string>
-
- <!-- Label of the print dialog's save button. [CHAR LIMIT=16] -->
- <string name="save_button">Save</string>
+ <!-- Label of the print dialog's button for more print options. [CHAR LIMIT=25] -->
+ <string name="more_options_button">More options</string>
<!-- Label of the destination widget. [CHAR LIMIT=20] -->
<string name="label_destination">Destination</string>
@@ -34,8 +28,14 @@
<!-- Label of the copies count widget. [CHAR LIMIT=20] -->
<string name="label_copies">Copies</string>
+ <!-- Label of the copies count for the print options summary. [CHAR LIMIT=20] -->
+ <string name="label_copies_summary">Copies:</string>
+
<!-- Label of the paper size widget. [CHAR LIMIT=20] -->
- <string name="label_paper_size">Paper Size</string>
+ <string name="label_paper_size">Paper size</string>
+
+ <!-- Label of the paper size for the print options summary. [CHAR LIMIT=20] -->
+ <string name="label_paper_size_summary">Paper size:</string>
<!-- Label of the color mode widget. [CHAR LIMIT=20] -->
<string name="label_color">Color</string>
@@ -118,19 +118,19 @@
<!-- Notifications -->
- <!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->
+ <!-- Template for the notification label for a printing print job. [CHAR LIMIT=25] -->
<string name="printing_notification_title_template">Printing <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
- <!-- Template for the notificaiton label for a cancelling print job. [CHAR LIMIT=25] -->
+ <!-- Template for the notification label for a cancelling print job. [CHAR LIMIT=25] -->
<string name="cancelling_notification_title_template">Cancelling <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
- <!-- Template for the notificaiton label for a failed print job. [CHAR LIMIT=25] -->
+ <!-- Template for the notification label for a failed print job. [CHAR LIMIT=25] -->
<string name="failed_notification_title_template">Printer error <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
- <!-- Template for the notificaiton label for a blocked print job. [CHAR LIMIT=25] -->
+ <!-- Template for the notification label for a blocked print job. [CHAR LIMIT=25] -->
<string name="blocked_notification_title_template">Printer blocked <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
- <!-- Template for the notificaiton label for a composite (multiple items) print jobs notification. [CHAR LIMIT=25] -->
+ <!-- Template for the notification label for a composite (multiple items) print jobs notification. [CHAR LIMIT=25] -->
<plurals name="composite_notification_title_template">
<item quantity="one"><xliff:g id="print_job_name" example="foo.jpg">%1$d</xliff:g> print job</item>
<item quantity="other"><xliff:g id="print_job_name" example="foo.jpg">%1$d</xliff:g> print jobs</item>
@@ -139,7 +139,7 @@
<!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] -->
<string name="cancel">Cancel</string>
- <!-- Label for the notification button for restrating a filed print job. [CHAR LIMIT=25] -->
+ <!-- Label for the notification button for restarting a filed print job. [CHAR LIMIT=25] -->
<string name="restart">Restart</string>
<!-- Message that there is no connection to a printer. [CHAR LIMIT=40] -->
@@ -151,9 +151,6 @@
<!-- Label for a printer that is not available. [CHAR LIMIT=25] -->
<string name="printer_unavailable"><xliff:g id="print_job_name" example="Canon-123GHT">%1$s</xliff:g> – unavailable</string>
- <!-- Default message of an alert dialog for app error while generating a print job. [CHAR LIMIT=50] -->
- <string name="print_error_default_message">Couldn\'t generate print job</string>
-
<!-- Arrays -->
<!-- Color mode labels. -->
@@ -200,4 +197,23 @@
holder to start the configuration activities of a print service. Should never be needed
for normal apps.</string>
+ <!-- Error messages -->
+
+ <!-- Message for an error when trying to print to a PDF file. [CHAR LIMIT=50] -->
+ <string name="print_write_error_message">Couldn\'t write to file</string>
+
+ <!-- Default message for an error while generating a print job. [CHAR LIMIT=50] -->
+ <string name="print_error_default_message">Couldn\'t generate print job</string>
+
+ <!-- Label for the retry button in the error message. [CHAR LIMIT=50] -->
+ <string name="print_error_retry">Retry</string>
+
+ <!-- Message for the currently selected printer becoming unavailable. [CHAR LIMIT=50] -->
+ <string name="print_error_printer_unavailable">Printer unavailable</string>
+
+ <!-- Long running operations -->
+
+ <!-- Message for the cancel print operation UI while waiting for cancel to be performed. [CHAR LIMIT=50] -->
+ <string name="print_operation_canceling">Cancelling\u2026</string>
+
</resources>
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
index d64380a..9637847 100644
--- a/packages/PrintSpooler/res/values/styles.xml
+++ b/packages/PrintSpooler/res/values/styles.xml
@@ -16,13 +16,6 @@
<resources>
- <style name="PrintOptionTitleTextAppearance">
- <item name="android:textStyle">normal</item>
- <item name="android:textSize">14sp</item>
- <item name="android:textAllCaps">true</item>
- <item name="android:textColor">@color/print_option_title</item>
- </style>
-
<style name="PrintOptionSpinnerStyle">
<item name="android:paddingTop">0dip</item>
<item name="android:paddingBottom">0dip</item>
@@ -30,7 +23,7 @@
</style>
<style name="PrintOptionEditTextStyle">
- <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
+
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
</style>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index 94ab895..40bf725 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -16,15 +16,6 @@
<resources>
- <style name="PrintJobConfigActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:windowSoftInputMode">stateAlwaysHidden|adjustResize</item>
- <item name="android:windowIsTranslucent">true</item>
- <item name="android:backgroundDimEnabled">true</item>
- <item name="android:colorBackgroundCacheHint">@android:color/transparent</item>
- <item name="android:windowIsFloating">true</item>
- </style>
-
<style name="SelectPrinterActivityTheme" parent="@android:style/Theme.DeviceDefault.Light">
<item name="android:actionBarStyle">@style/SelectPrinterActivityActionBarStyle</item>
</style>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
deleted file mode 100644
index 9831839..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
+++ /dev/null
@@ -1,708 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Loader;
-import android.content.pm.ServiceInfo;
-import android.os.AsyncTask;
-import android.print.PrintManager;
-import android.print.PrinterDiscoverySession;
-import android.print.PrinterDiscoverySession.OnPrintersChangeListener;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
-import android.printservice.PrintServiceInfo;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.util.FastXmlSerializer;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import libcore.io.IoUtils;
-
-/**
- * This class is responsible for loading printers by doing discovery
- * and merging the discovered printers with the previously used ones.
- */
-public class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
- private static final String LOG_TAG = "FusedPrintersProvider";
-
- private static final boolean DEBUG = false;
-
- private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f;
- private static final int MAX_HISTORY_LENGTH = 50;
-
- private static final int MAX_FAVORITE_PRINTER_COUNT = 4;
-
- private final List<PrinterInfo> mPrinters =
- new ArrayList<PrinterInfo>();
-
- private final List<PrinterInfo> mFavoritePrinters =
- new ArrayList<PrinterInfo>();
-
- private final PersistenceManager mPersistenceManager;
-
- private PrinterDiscoverySession mDiscoverySession;
-
- private PrinterId mTrackedPrinter;
-
- private boolean mPrintersUpdatedBefore;
-
- public FusedPrintersProvider(Context context) {
- super(context);
- mPersistenceManager = new PersistenceManager(context);
- }
-
- public void addHistoricalPrinter(PrinterInfo printer) {
- mPersistenceManager.addPrinterAndWritePrinterHistory(printer);
- }
-
- private void computeAndDeliverResult(ArrayMap<PrinterId, PrinterInfo> discoveredPrinters,
- ArrayMap<PrinterId, PrinterInfo> favoritePrinters) {
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
-
- // Add the updated favorite printers.
- final int favoritePrinterCount = favoritePrinters.size();
- for (int i = 0; i < favoritePrinterCount; i++) {
- PrinterInfo favoritePrinter = favoritePrinters.valueAt(i);
- PrinterInfo updatedPrinter = discoveredPrinters.remove(
- favoritePrinter.getId());
- if (updatedPrinter != null) {
- printers.add(updatedPrinter);
- } else {
- printers.add(favoritePrinter);
- }
- }
-
- // Add other updated printers.
- final int printerCount = mPrinters.size();
- for (int i = 0; i < printerCount; i++) {
- PrinterInfo printer = mPrinters.get(i);
- PrinterInfo updatedPrinter = discoveredPrinters.remove(
- printer.getId());
- if (updatedPrinter != null) {
- printers.add(updatedPrinter);
- }
- }
-
- // Add the new printers, i.e. what is left.
- printers.addAll(discoveredPrinters.values());
-
- // Update the list of printers.
- mPrinters.clear();
- mPrinters.addAll(printers);
-
- if (isStarted()) {
- // If stated deliver the new printers.
- deliverResult(printers);
- } else {
- // Otherwise, take a note for the change.
- onContentChanged();
- }
- }
-
- @Override
- protected void onStartLoading() {
- if (DEBUG) {
- Log.i(LOG_TAG, "onStartLoading() " + FusedPrintersProvider.this.hashCode());
- }
- // The contract is that if we already have a valid,
- // result the we have to deliver it immediately.
- if (!mPrinters.isEmpty()) {
- deliverResult(new ArrayList<PrinterInfo>(mPrinters));
- }
- // Always load the data to ensure discovery period is
- // started and to make sure obsolete printers are updated.
- onForceLoad();
- }
-
- @Override
- protected void onStopLoading() {
- if (DEBUG) {
- Log.i(LOG_TAG, "onStopLoading() " + FusedPrintersProvider.this.hashCode());
- }
- onCancelLoad();
- }
-
- @Override
- protected void onForceLoad() {
- if (DEBUG) {
- Log.i(LOG_TAG, "onForceLoad() " + FusedPrintersProvider.this.hashCode());
- }
- loadInternal();
- }
-
- private void loadInternal() {
- if (mDiscoverySession == null) {
- PrintManager printManager = (PrintManager) getContext()
- .getSystemService(Context.PRINT_SERVICE);
- mDiscoverySession = printManager.createPrinterDiscoverySession();
- mPersistenceManager.readPrinterHistory();
- } else if (mPersistenceManager.isHistoryChanged()) {
- mPersistenceManager.readPrinterHistory();
- }
- if (mPersistenceManager.isReadHistoryCompleted()
- && !mDiscoverySession.isPrinterDiscoveryStarted()) {
- mDiscoverySession.setOnPrintersChangeListener(new OnPrintersChangeListener() {
- @Override
- public void onPrintersChanged() {
- if (DEBUG) {
- Log.i(LOG_TAG, "onPrintersChanged() count:"
- + mDiscoverySession.getPrinters().size()
- + " " + FusedPrintersProvider.this.hashCode());
- }
- updatePrinters(mDiscoverySession.getPrinters(), mFavoritePrinters);
- }
- });
- final int favoriteCount = mFavoritePrinters.size();
- List<PrinterId> printerIds = new ArrayList<PrinterId>(favoriteCount);
- for (int i = 0; i < favoriteCount; i++) {
- printerIds.add(mFavoritePrinters.get(i).getId());
- }
- mDiscoverySession.startPrinterDisovery(printerIds);
- List<PrinterInfo> printers = mDiscoverySession.getPrinters();
- if (!printers.isEmpty()) {
- updatePrinters(printers, mFavoritePrinters);
- }
- }
- }
-
- private void updatePrinters(List<PrinterInfo> printers, List<PrinterInfo> favoritePrinters) {
- if (mPrintersUpdatedBefore && mPrinters.equals(printers)
- && mFavoritePrinters.equals(favoritePrinters)) {
- return;
- }
-
- mPrintersUpdatedBefore = true;
-
- ArrayMap<PrinterId, PrinterInfo> printersMap =
- new ArrayMap<PrinterId, PrinterInfo>();
- final int printerCount = printers.size();
- for (int i = 0; i < printerCount; i++) {
- PrinterInfo printer = printers.get(i);
- printersMap.put(printer.getId(), printer);
- }
-
- ArrayMap<PrinterId, PrinterInfo> favoritePrintersMap =
- new ArrayMap<PrinterId, PrinterInfo>();
- final int favoritePrinterCount = favoritePrinters.size();
- for (int i = 0; i < favoritePrinterCount; i++) {
- PrinterInfo favoritePrinter = favoritePrinters.get(i);
- favoritePrintersMap.put(favoritePrinter.getId(), favoritePrinter);
- }
-
- computeAndDeliverResult(printersMap, favoritePrintersMap);
- }
-
- @Override
- protected boolean onCancelLoad() {
- if (DEBUG) {
- Log.i(LOG_TAG, "onCancelLoad() " + FusedPrintersProvider.this.hashCode());
- }
- return cancelInternal();
- }
-
- private boolean cancelInternal() {
- if (mDiscoverySession != null
- && mDiscoverySession.isPrinterDiscoveryStarted()) {
- if (mTrackedPrinter != null) {
- mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
- mTrackedPrinter = null;
- }
- mDiscoverySession.stopPrinterDiscovery();
- return true;
- } else if (mPersistenceManager.isReadHistoryInProgress()) {
- return mPersistenceManager.stopReadPrinterHistory();
- }
- return false;
- }
-
- @Override
- protected void onReset() {
- if (DEBUG) {
- Log.i(LOG_TAG, "onReset() " + FusedPrintersProvider.this.hashCode());
- }
- onStopLoading();
- mPrinters.clear();
- if (mDiscoverySession != null) {
- mDiscoverySession.destroy();
- mDiscoverySession = null;
- }
- }
-
- @Override
- protected void onAbandon() {
- if (DEBUG) {
- Log.i(LOG_TAG, "onAbandon() " + FusedPrintersProvider.this.hashCode());
- }
- onStopLoading();
- }
-
- public void setTrackedPrinter(PrinterId printerId) {
- if (isStarted() && mDiscoverySession != null
- && mDiscoverySession.isPrinterDiscoveryStarted()) {
- if (mTrackedPrinter != null) {
- if (mTrackedPrinter.equals(printerId)) {
- return;
- }
- mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
- }
- mTrackedPrinter = printerId;
- mDiscoverySession.startPrinterStateTracking(printerId);
- }
- }
-
- public boolean isFavoritePrinter(PrinterId printerId) {
- final int printerCount = mFavoritePrinters.size();
- for (int i = 0; i < printerCount; i++) {
- PrinterInfo favoritePritner = mFavoritePrinters.get(i);
- if (favoritePritner.getId().equals(printerId)) {
- return true;
- }
- }
- return false;
- }
-
- public void forgetFavoritePrinter(PrinterId printerId) {
- List<PrinterInfo> newFavoritePrinters = null;
-
- // Remove the printer from the favorites.
- final int favoritePrinterCount = mFavoritePrinters.size();
- for (int i = 0; i < favoritePrinterCount; i++) {
- PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
- if (favoritePrinter.getId().equals(printerId)) {
- newFavoritePrinters = new ArrayList<PrinterInfo>();
- newFavoritePrinters.addAll(mPrinters);
- newFavoritePrinters.remove(i);
- break;
- }
- }
-
- // If we removed a favorite printer, we have work to do.
- if (newFavoritePrinters != null) {
- // Remove the printer from history and persist the latter.
- mPersistenceManager.removeHistoricalPrinterAndWritePrinterHistory(printerId);
-
- // Recompute and deliver the printers.
- updatePrinters(mDiscoverySession.getPrinters(), newFavoritePrinters);
- }
- }
-
- private final class PersistenceManager {
- private static final String PERSIST_FILE_NAME = "printer_history.xml";
-
- private static final String TAG_PRINTERS = "printers";
-
- private static final String TAG_PRINTER = "printer";
- private static final String TAG_PRINTER_ID = "printerId";
-
- private static final String ATTR_LOCAL_ID = "localId";
- private static final String ATTR_SERVICE_NAME = "serviceName";
-
- private static final String ATTR_NAME = "name";
- private static final String ATTR_DESCRIPTION = "description";
- private static final String ATTR_STATUS = "status";
-
- private final AtomicFile mStatePersistFile;
-
- private List<PrinterInfo> mHistoricalPrinters = new ArrayList<PrinterInfo>();
-
- private boolean mReadHistoryCompleted;
- private boolean mReadHistoryInProgress;
-
- private ReadTask mReadTask;
-
- private volatile long mLastReadHistoryTimestamp;
-
- private PersistenceManager(Context context) {
- mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
- PERSIST_FILE_NAME));
- }
-
- public boolean isReadHistoryInProgress() {
- return mReadHistoryInProgress;
- }
-
- public boolean isReadHistoryCompleted() {
- return mReadHistoryCompleted;
- }
-
- public boolean stopReadPrinterHistory() {
- final boolean cancelled = mReadTask.cancel(true);
- mReadTask = null;
- return cancelled;
- }
-
- public void readPrinterHistory() {
- if (DEBUG) {
- Log.i(LOG_TAG, "read history started "
- + FusedPrintersProvider.this.hashCode());
- }
- mReadHistoryInProgress = true;
- mReadTask = new ReadTask();
- mReadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
- }
-
- @SuppressWarnings("unchecked")
- public void addPrinterAndWritePrinterHistory(PrinterInfo printer) {
- if (mHistoricalPrinters.size() >= MAX_HISTORY_LENGTH) {
- mHistoricalPrinters.remove(0);
- }
- mHistoricalPrinters.add(printer);
- new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
- new ArrayList<PrinterInfo>(mHistoricalPrinters));
- }
-
- @SuppressWarnings("unchecked")
- public void removeHistoricalPrinterAndWritePrinterHistory(PrinterId printerId) {
- boolean writeHistory = false;
- final int printerCount = mHistoricalPrinters.size();
- for (int i = printerCount - 1; i >= 0; i--) {
- PrinterInfo historicalPrinter = mHistoricalPrinters.get(i);
- if (historicalPrinter.getId().equals(printerId)) {
- mHistoricalPrinters.remove(i);
- writeHistory = true;
- }
- }
- if (writeHistory) {
- new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
- new ArrayList<PrinterInfo>(mHistoricalPrinters));
- }
- }
-
- public boolean isHistoryChanged() {
- return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
- }
-
- private List<PrinterInfo> computeFavoritePrinters(List<PrinterInfo> printers) {
- Map<PrinterId, PrinterRecord> recordMap =
- new ArrayMap<PrinterId, PrinterRecord>();
-
- // Recompute the weights.
- float currentWeight = 1.0f;
- final int printerCount = printers.size();
- for (int i = printerCount - 1; i >= 0; i--) {
- PrinterInfo printer = printers.get(i);
- // Aggregate weight for the same printer
- PrinterRecord record = recordMap.get(printer.getId());
- if (record == null) {
- record = new PrinterRecord(printer);
- recordMap.put(printer.getId(), record);
- }
- record.weight += currentWeight;
- currentWeight *= WEIGHT_DECAY_COEFFICIENT;
- }
-
- // Soft the favorite printers.
- List<PrinterRecord> favoriteRecords = new ArrayList<PrinterRecord>(
- recordMap.values());
- Collections.sort(favoriteRecords);
-
- // Write the favorites to the output.
- final int favoriteCount = Math.min(favoriteRecords.size(),
- MAX_FAVORITE_PRINTER_COUNT);
- List<PrinterInfo> favoritePrinters = new ArrayList<PrinterInfo>(favoriteCount);
- for (int i = 0; i < favoriteCount; i++) {
- PrinterInfo printer = favoriteRecords.get(i).printer;
- favoritePrinters.add(printer);
- }
-
- return favoritePrinters;
- }
-
- private final class PrinterRecord implements Comparable<PrinterRecord> {
- public final PrinterInfo printer;
- public float weight;
-
- public PrinterRecord(PrinterInfo printer) {
- this.printer = printer;
- }
-
- @Override
- public int compareTo(PrinterRecord another) {
- return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
- }
- }
-
- private final class ReadTask extends AsyncTask<Void, Void, List<PrinterInfo>> {
- @Override
- protected List<PrinterInfo> doInBackground(Void... args) {
- return doReadPrinterHistory();
- }
-
- @Override
- protected void onPostExecute(List<PrinterInfo> printers) {
- if (DEBUG) {
- Log.i(LOG_TAG, "read history completed "
- + FusedPrintersProvider.this.hashCode());
- }
-
- // Ignore printer records whose target services are not enabled.
- PrintManager printManager = (PrintManager) getContext()
- .getSystemService(Context.PRINT_SERVICE);
- List<PrintServiceInfo> services = printManager
- .getEnabledPrintServices();
-
- Set<ComponentName> enabledComponents = new ArraySet<ComponentName>();
- final int installedServiceCount = services.size();
- for (int i = 0; i < installedServiceCount; i++) {
- ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
- ComponentName componentName = new ComponentName(
- serviceInfo.packageName, serviceInfo.name);
- enabledComponents.add(componentName);
- }
-
- final int printerCount = printers.size();
- for (int i = printerCount - 1; i >= 0; i--) {
- ComponentName printerServiceName = printers.get(i).getId().getServiceName();
- if (!enabledComponents.contains(printerServiceName)) {
- printers.remove(i);
- }
- }
-
- // Store the filtered list.
- mHistoricalPrinters = printers;
-
- // Compute the favorite printers.
- mFavoritePrinters.clear();
- mFavoritePrinters.addAll(computeFavoritePrinters(mHistoricalPrinters));
-
- mReadHistoryInProgress = false;
- mReadHistoryCompleted = true;
-
- // Deliver the printers.
- updatePrinters(mDiscoverySession.getPrinters(), mHistoricalPrinters);
-
- // Loading the available printers if needed.
- loadInternal();
-
- // We are done.
- mReadTask = null;
- }
-
- private List<PrinterInfo> doReadPrinterHistory() {
- FileInputStream in = null;
- try {
- in = mStatePersistFile.openRead();
- } catch (FileNotFoundException fnfe) {
- if (DEBUG) {
- Log.i(LOG_TAG, "No existing printer history "
- + FusedPrintersProvider.this.hashCode());
- }
- return new ArrayList<PrinterInfo>();
- }
- try {
- List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, null);
- parseState(parser, printers);
- // Take a note which version of the history was read.
- mLastReadHistoryTimestamp = mStatePersistFile.getBaseFile().lastModified();
- return printers;
- } catch (IllegalStateException ise) {
- Slog.w(LOG_TAG, "Failed parsing ", ise);
- } catch (NullPointerException npe) {
- Slog.w(LOG_TAG, "Failed parsing ", npe);
- } catch (NumberFormatException nfe) {
- Slog.w(LOG_TAG, "Failed parsing ", nfe);
- } catch (XmlPullParserException xppe) {
- Slog.w(LOG_TAG, "Failed parsing ", xppe);
- } catch (IOException ioe) {
- Slog.w(LOG_TAG, "Failed parsing ", ioe);
- } catch (IndexOutOfBoundsException iobe) {
- Slog.w(LOG_TAG, "Failed parsing ", iobe);
- } finally {
- IoUtils.closeQuietly(in);
- }
-
- return Collections.emptyList();
- }
-
- private void parseState(XmlPullParser parser, List<PrinterInfo> outPrinters)
- throws IOException, XmlPullParserException {
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_PRINTERS);
- parser.next();
-
- while (parsePrinter(parser, outPrinters)) {
- // Be nice and respond to cancellation
- if (isCancelled()) {
- return;
- }
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_PRINTERS);
- }
-
- private boolean parsePrinter(XmlPullParser parser, List<PrinterInfo> outPrinters)
- throws IOException, XmlPullParserException {
- skipEmptyTextTags(parser);
- if (!accept(parser, XmlPullParser.START_TAG, TAG_PRINTER)) {
- return false;
- }
-
- String name = parser.getAttributeValue(null, ATTR_NAME);
- String description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
- final int status = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATUS));
-
- parser.next();
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID);
- String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
- ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
- null, ATTR_SERVICE_NAME));
- PrinterId printerId = new PrinterId(service, localId);
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
- parser.next();
-
- PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId, name, status);
- builder.setDescription(description);
- PrinterInfo printer = builder.build();
-
- outPrinters.add(printer);
-
- if (DEBUG) {
- Log.i(LOG_TAG, "[RESTORED] " + printer);
- }
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
-
- return true;
- }
-
- private void expect(XmlPullParser parser, int type, String tag)
- throws IOException, XmlPullParserException {
- if (!accept(parser, type, tag)) {
- throw new XmlPullParserException("Exepected event: " + type
- + " and tag: " + tag + " but got event: " + parser.getEventType()
- + " and tag:" + parser.getName());
- }
- }
-
- private void skipEmptyTextTags(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- while (accept(parser, XmlPullParser.TEXT, null)
- && "\n".equals(parser.getText())) {
- parser.next();
- }
- }
-
- private boolean accept(XmlPullParser parser, int type, String tag)
- throws IOException, XmlPullParserException {
- if (parser.getEventType() != type) {
- return false;
- }
- if (tag != null) {
- if (!tag.equals(parser.getName())) {
- return false;
- }
- } else if (parser.getName() != null) {
- return false;
- }
- return true;
- }
- };
-
- private final class WriteTask extends AsyncTask<List<PrinterInfo>, Void, Void> {
- @Override
- protected Void doInBackground(List<PrinterInfo>... printers) {
- doWritePrinterHistory(printers[0]);
- return null;
- }
-
- private void doWritePrinterHistory(List<PrinterInfo> printers) {
- FileOutputStream out = null;
- try {
- out = mStatePersistFile.startWrite();
-
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(out, "utf-8");
- serializer.startDocument(null, true);
- serializer.startTag(null, TAG_PRINTERS);
-
- final int printerCount = printers.size();
- for (int i = 0; i < printerCount; i++) {
- PrinterInfo printer = printers.get(i);
-
- serializer.startTag(null, TAG_PRINTER);
-
- serializer.attribute(null, ATTR_NAME, printer.getName());
- // Historical printers are always stored as unavailable.
- serializer.attribute(null, ATTR_STATUS, String.valueOf(
- PrinterInfo.STATUS_UNAVAILABLE));
- String description = printer.getDescription();
- if (description != null) {
- serializer.attribute(null, ATTR_DESCRIPTION, description);
- }
-
- PrinterId printerId = printer.getId();
- serializer.startTag(null, TAG_PRINTER_ID);
- serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
- serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
- .flattenToString());
- serializer.endTag(null, TAG_PRINTER_ID);
-
- serializer.endTag(null, TAG_PRINTER);
-
- if (DEBUG) {
- Log.i(LOG_TAG, "[PERSISTED] " + printer);
- }
- }
-
- serializer.endTag(null, TAG_PRINTERS);
- serializer.endDocument();
- mStatePersistFile.finishWrite(out);
-
- if (DEBUG) {
- Log.i(LOG_TAG, "[PERSIST END]");
- }
- } catch (IOException ioe) {
- Slog.w(LOG_TAG, "Failed to write printer history, restoring backup.", ioe);
- mStatePersistFile.failWrite(out);
- } finally {
- IoUtils.closeQuietly(out);
- }
- }
- };
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/MediaSizeUtils.java b/packages/PrintSpooler/src/com/android/printspooler/MediaSizeUtils.java
deleted file mode 100644
index ac27562..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/MediaSizeUtils.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.content.Context;
-import android.print.PrintAttributes.MediaSize;
-import android.util.ArrayMap;
-
-import java.util.Comparator;
-import java.util.Map;
-
-/**
- * Utility functions and classes for dealing with media sizes.
- */
-public class MediaSizeUtils {
-
- private static Map<MediaSize, String> sMediaSizeToStandardMap;
-
- /**
- * Gets the default media size for the current locale.
- *
- * @param context Context for accessing resources.
- * @return The default media size.
- */
- public static MediaSize getDefault(Context context) {
- String mediaSizeId = context.getString(R.string.mediasize_default);
- return MediaSize.getStandardMediaSizeById(mediaSizeId);
- }
-
- private static String getStandardForMediaSize(Context context, MediaSize mediaSize) {
- if (sMediaSizeToStandardMap == null) {
- sMediaSizeToStandardMap = new ArrayMap<MediaSize, String>();
- String[] mediaSizeToStandardMapValues = context.getResources()
- .getStringArray(R.array.mediasize_to_standard_map);
- final int mediaSizeToStandardCount = mediaSizeToStandardMapValues.length;
- for (int i = 0; i < mediaSizeToStandardCount; i += 2) {
- String mediaSizeId = mediaSizeToStandardMapValues[i];
- MediaSize key = MediaSize.getStandardMediaSizeById(mediaSizeId);
- String value = mediaSizeToStandardMapValues[i + 1];
- sMediaSizeToStandardMap.put(key, value);
- }
- }
- String standard = sMediaSizeToStandardMap.get(mediaSize);
- return (standard != null) ? standard : context.getString(
- R.string.mediasize_standard_iso);
- }
-
- /**
- * Comparator for ordering standard media sizes. The ones for the current
- * standard go to the top and the ones for the other standards follow grouped
- * by standard. Media sizes of the same standard are ordered alphabetically.
- */
- public static final class MediaSizeComparator implements Comparator<MediaSize> {
- private final Context mContext;
-
- public MediaSizeComparator(Context context) {
- mContext = context;
- }
-
- @Override
- public int compare(MediaSize lhs, MediaSize rhs) {
- String currentStandard = mContext.getString(R.string.mediasize_standard);
- String lhsStandard = getStandardForMediaSize(mContext, lhs);
- String rhsStandard = getStandardForMediaSize(mContext, rhs);
-
- // The current standard always wins.
- if (lhsStandard.equals(currentStandard)) {
- if (!rhsStandard.equals(currentStandard)) {
- return -1;
- }
- } else if (rhsStandard.equals(currentStandard)) {
- return 1;
- }
-
- if (!lhsStandard.equals(rhsStandard)) {
- // Different standards - use the standard ordering.
- return lhsStandard.compareTo(rhsStandard);
- } else {
- // Same standard - sort alphabetically by label.
- return lhs.getLabel(mContext.getPackageManager()).
- compareTo(rhs.getLabel(mContext.getPackageManager()));
- }
- }
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
deleted file mode 100644
index 968a8bf..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.app.Notification;
-import android.app.Notification.InboxStyle;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.print.IPrintManager;
-import android.print.PrintJobId;
-import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class is responsible for updating the print notifications
- * based on print job state transitions.
- */
-public class NotificationController {
- public static final boolean DEBUG = false;
-
- public static final String LOG_TAG = "NotificationController";
-
- private static final String INTENT_ACTION_CANCEL_PRINTJOB = "INTENT_ACTION_CANCEL_PRINTJOB";
- private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB";
-
- private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID";
- private static final String EXTRA_PRINTJOB_LABEL = "EXTRA_PRINTJOB_LABEL";
- private static final String EXTRA_PRINTER_NAME = "EXTRA_PRINTER_NAME";
-
- private final Context mContext;
- private final NotificationManager mNotificationManager;
-
- public NotificationController(Context context) {
- mContext = context;
- mNotificationManager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- public void onUpdateNotifications(List<PrintJobInfo> printJobs) {
- List<PrintJobInfo> notifyPrintJobs = new ArrayList<PrintJobInfo>();
-
- final int printJobCount = printJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = printJobs.get(i);
- if (shouldNotifyForState(printJob.getState())) {
- notifyPrintJobs.add(printJob);
- }
- }
-
- updateNotification(notifyPrintJobs);
- }
-
- private void updateNotification(List<PrintJobInfo> printJobs) {
- if (printJobs.size() <= 0) {
- removeNotification();
- } else if (printJobs.size() == 1) {
- createSimpleNotification(printJobs.get(0));
- } else {
- createStackedNotification(printJobs);
- }
- }
-
- private void createSimpleNotification(PrintJobInfo printJob) {
- switch (printJob.getState()) {
- case PrintJobInfo.STATE_FAILED: {
- createFailedNotification(printJob);
- } break;
-
- case PrintJobInfo.STATE_BLOCKED: {
- if (!printJob.isCancelling()) {
- createBlockedNotification(printJob);
- } else {
- createCancellingNotification(printJob);
- }
- } break;
-
- default: {
- if (!printJob.isCancelling()) {
- createPrintingNotification(printJob);
- } else {
- createCancellingNotification(printJob);
- }
- } break;
- }
- }
-
- private void createPrintingNotification(PrintJobInfo printJob) {
- Notification.Builder builder = new Notification.Builder(mContext)
- .setContentIntent(createContentIntent(printJob.getId()))
- .setSmallIcon(computeNotificationIcon(printJob))
- .setContentTitle(computeNotificationTitle(printJob))
- .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
- createCancelIntent(printJob))
- .setContentText(printJob.getPrinterName())
- .setWhen(System.currentTimeMillis())
- .setOngoing(true)
- .setShowWhen(true);
- mNotificationManager.notify(0, builder.build());
- }
-
- private void createFailedNotification(PrintJobInfo printJob) {
- Notification.Builder builder = new Notification.Builder(mContext)
- .setContentIntent(createContentIntent(printJob.getId()))
- .setSmallIcon(computeNotificationIcon(printJob))
- .setContentTitle(computeNotificationTitle(printJob))
- .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
- createCancelIntent(printJob))
- .addAction(R.drawable.ic_restart, mContext.getString(R.string.restart),
- createRestartIntent(printJob.getId()))
- .setContentText(printJob.getPrinterName())
- .setWhen(System.currentTimeMillis())
- .setOngoing(true)
- .setShowWhen(true);
- mNotificationManager.notify(0, builder.build());
- }
-
- private void createBlockedNotification(PrintJobInfo printJob) {
- Notification.Builder builder = new Notification.Builder(mContext)
- .setContentIntent(createContentIntent(printJob.getId()))
- .setSmallIcon(computeNotificationIcon(printJob))
- .setContentTitle(computeNotificationTitle(printJob))
- .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
- createCancelIntent(printJob))
- .setContentText(printJob.getPrinterName())
- .setWhen(System.currentTimeMillis())
- .setOngoing(true)
- .setShowWhen(true);
- mNotificationManager.notify(0, builder.build());
- }
-
- private void createCancellingNotification(PrintJobInfo printJob) {
- Notification.Builder builder = new Notification.Builder(mContext)
- .setContentIntent(createContentIntent(printJob.getId()))
- .setSmallIcon(computeNotificationIcon(printJob))
- .setContentTitle(computeNotificationTitle(printJob))
- .setContentText(printJob.getPrinterName())
- .setWhen(System.currentTimeMillis())
- .setOngoing(true)
- .setShowWhen(true);
- mNotificationManager.notify(0, builder.build());
- }
-
- private void createStackedNotification(List<PrintJobInfo> printJobs) {
- Notification.Builder builder = new Notification.Builder(mContext)
- .setContentIntent(createContentIntent(null))
- .setWhen(System.currentTimeMillis())
- .setOngoing(true)
- .setShowWhen(true);
-
- final int printJobCount = printJobs.size();
-
- InboxStyle inboxStyle = new InboxStyle();
- inboxStyle.setBigContentTitle(String.format(mContext.getResources().getQuantityText(
- R.plurals.composite_notification_title_template,
- printJobCount).toString(), printJobCount));
-
- for (int i = printJobCount - 1; i>= 0; i--) {
- PrintJobInfo printJob = printJobs.get(i);
- if (i == printJobCount - 1) {
- builder.setLargeIcon(((BitmapDrawable) mContext.getResources().getDrawable(
- computeNotificationIcon(printJob))).getBitmap());
- builder.setSmallIcon(computeNotificationIcon(printJob));
- builder.setContentTitle(computeNotificationTitle(printJob));
- builder.setContentText(printJob.getPrinterName());
- }
- inboxStyle.addLine(computeNotificationTitle(printJob));
- }
-
- builder.setNumber(printJobCount);
- builder.setStyle(inboxStyle);
-
- mNotificationManager.notify(0, builder.build());
- }
-
- private String computeNotificationTitle(PrintJobInfo printJob) {
- switch (printJob.getState()) {
- case PrintJobInfo.STATE_FAILED: {
- return mContext.getString(R.string.failed_notification_title_template,
- printJob.getLabel());
- }
-
- case PrintJobInfo.STATE_BLOCKED: {
- if (!printJob.isCancelling()) {
- return mContext.getString(R.string.blocked_notification_title_template,
- printJob.getLabel());
- } else {
- return mContext.getString(
- R.string.cancelling_notification_title_template,
- printJob.getLabel());
- }
- }
-
- default: {
- if (!printJob.isCancelling()) {
- return mContext.getString(R.string.printing_notification_title_template,
- printJob.getLabel());
- } else {
- return mContext.getString(
- R.string.cancelling_notification_title_template,
- printJob.getLabel());
- }
- }
- }
- }
-
- private void removeNotification() {
- mNotificationManager.cancel(0);
- }
-
- private PendingIntent createContentIntent(PrintJobId printJobId) {
- Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
- if (printJobId != null) {
- intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString());
- intent.setData(Uri.fromParts("printjob", printJobId.flattenToString(), null));
- }
- return PendingIntent.getActivity(mContext, 0, intent, 0);
- }
-
- private PendingIntent createCancelIntent(PrintJobInfo printJob) {
- Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
- intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + printJob.getId().flattenToString());
- intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId());
- intent.putExtra(EXTRA_PRINTJOB_LABEL, printJob.getLabel());
- intent.putExtra(EXTRA_PRINTER_NAME, printJob.getPrinterName());
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
- }
-
- private PendingIntent createRestartIntent(PrintJobId printJobId) {
- Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
- intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + printJobId.flattenToString());
- intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId);
- return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
- }
-
- private static boolean shouldNotifyForState(int state) {
- switch (state) {
- case PrintJobInfo.STATE_QUEUED:
- case PrintJobInfo.STATE_STARTED:
- case PrintJobInfo.STATE_FAILED:
- case PrintJobInfo.STATE_COMPLETED:
- case PrintJobInfo.STATE_CANCELED:
- case PrintJobInfo.STATE_BLOCKED: {
- return true;
- }
- }
- return false;
- }
-
- private static int computeNotificationIcon(PrintJobInfo printJob) {
- switch (printJob.getState()) {
- case PrintJobInfo.STATE_FAILED:
- case PrintJobInfo.STATE_BLOCKED: {
- return com.android.internal.R.drawable.ic_print_error;
- }
- default: {
- if (!printJob.isCancelling()) {
- return com.android.internal.R.drawable.ic_print;
- } else {
- return R.drawable.stat_notify_cancelling;
- }
- }
- }
- }
-
- public static final class NotificationBroadcastReceiver extends BroadcastReceiver {
- private static final String LOG_TAG = "NotificationBroadcastReceiver";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action != null && action.startsWith(INTENT_ACTION_CANCEL_PRINTJOB)) {
- PrintJobId printJobId = intent.getExtras().getParcelable(EXTRA_PRINT_JOB_ID);
- String printJobLabel = intent.getExtras().getString(EXTRA_PRINTJOB_LABEL);
- String printerName = intent.getExtras().getString(EXTRA_PRINTER_NAME);
- handleCancelPrintJob(context, printJobId, printJobLabel, printerName);
- } else if (action != null && action.startsWith(INTENT_ACTION_RESTART_PRINTJOB)) {
- PrintJobId printJobId = intent.getExtras().getParcelable(EXTRA_PRINT_JOB_ID);
- handleRestartPrintJob(context, printJobId);
- }
- }
-
- private void handleCancelPrintJob(final Context context, final PrintJobId printJobId,
- final String printJobLabel, final String printerName) {
- if (DEBUG) {
- Log.i(LOG_TAG, "handleCancelPrintJob() printJobId:" + printJobId);
- }
-
- // Call into the print manager service off the main thread since
- // the print manager service may end up binding to the print spooler
- // service which binding is handled on the main thread.
- PowerManager powerManager = (PowerManager)
- context.getSystemService(Context.POWER_SERVICE);
- final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- LOG_TAG);
- wakeLock.acquire();
-
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- // We need to request the cancellation to be done by the print
- // manager service since it has to communicate with the managing
- // print service to request the cancellation. Also we need the
- // system service to be bound to the spooler since canceling a
- // print job will trigger persistence of current jobs which is
- // done on another thread and until it finishes the spooler has
- // to be kept around.
- try {
- IPrintManager printManager = IPrintManager.Stub.asInterface(
- ServiceManager.getService(Context.PRINT_SERVICE));
- printManager.cancelPrintJob(printJobId, PrintManager.APP_ID_ANY,
- UserHandle.myUserId());
- } catch (RemoteException re) {
- Log.i(LOG_TAG, "Error requestion print job cancellation", re);
- } finally {
- wakeLock.release();
- }
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
-
- private void handleRestartPrintJob(final Context context, final PrintJobId printJobId) {
- if (DEBUG) {
- Log.i(LOG_TAG, "handleRestartPrintJob() printJobId:" + printJobId);
- }
-
- // Call into the print manager service off the main thread since
- // the print manager service may end up binding to the print spooler
- // service which binding is handled on the main thread.
- PowerManager powerManager = (PowerManager)
- context.getSystemService(Context.POWER_SERVICE);
- final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- LOG_TAG);
- wakeLock.acquire();
-
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- // We need to request the restart to be done by the print manager
- // service since the latter must be bound to the spooler because
- // restarting a print job will trigger persistence of current jobs
- // which is done on another thread and until it finishes the spooler has
- // to be kept around.
- try {
- IPrintManager printManager = IPrintManager.Stub.asInterface(
- ServiceManager.getService(Context.PRINT_SERVICE));
- printManager.restartPrintJob(printJobId, PrintManager.APP_ID_ANY,
- UserHandle.myUserId());
- } catch (RemoteException re) {
- Log.i(LOG_TAG, "Error requestion print job restart", re);
- } finally {
- wakeLock.release();
- }
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintDialogFrame.java b/packages/PrintSpooler/src/com/android/printspooler/PrintDialogFrame.java
deleted file mode 100644
index c1c4d21..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintDialogFrame.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-public class PrintDialogFrame extends FrameLayout {
-
- public final int mMaxWidth;
-
- public int mHeight;
-
- public PrintDialogFrame(Context context, AttributeSet attrs) {
- super(context, attrs);
- mMaxWidth = context.getResources().getDimensionPixelSize(
- R.dimen.print_dialog_frame_max_width_dip);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- int measuredWidth = getMeasuredWidth();
- final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- switch (widthMode) {
- case MeasureSpec.UNSPECIFIED: {
- measuredWidth = mMaxWidth;
- } break;
-
- case MeasureSpec.AT_MOST: {
- final int receivedWidth = MeasureSpec.getSize(widthMeasureSpec);
- measuredWidth = Math.min(mMaxWidth, receivedWidth);
- } break;
- }
-
- mHeight = Math.max(mHeight, getMeasuredHeight());
-
- int measuredHeight = getMeasuredHeight();
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- switch (heightMode) {
- case MeasureSpec.UNSPECIFIED: {
- measuredHeight = mHeight;
- } break;
-
- case MeasureSpec.AT_MOST: {
- final int receivedHeight = MeasureSpec.getSize(heightMeasureSpec);
- measuredHeight = Math.min(mHeight, receivedHeight);
- } break;
- }
-
- setMeasuredDimension(measuredWidth, measuredHeight);
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
deleted file mode 100644
index e3d8d05..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ /dev/null
@@ -1,2966 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.LoaderManager;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.ServiceConnection;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.database.DataSetObserver;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.print.ILayoutResultCallback;
-import android.print.IPrintDocumentAdapter;
-import android.print.IPrintDocumentAdapterObserver;
-import android.print.IWriteResultCallback;
-import android.print.PageRange;
-import android.print.PrintAttributes;
-import android.print.PrintAttributes.Margins;
-import android.print.PrintAttributes.MediaSize;
-import android.print.PrintAttributes.Resolution;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintDocumentInfo;
-import android.print.PrintJobId;
-import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.print.PrinterCapabilitiesInfo;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
-import android.printservice.PrintService;
-import android.printservice.PrintServiceInfo;
-import android.provider.DocumentsContract;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
-import android.text.TextWatcher;
-import android.util.ArrayMap;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.MeasureSpec;
-import android.view.View.OnAttachStateChangeListener;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewPropertyAnimator;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.ArrayAdapter;
-import android.widget.BaseAdapter;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import com.android.printspooler.MediaSizeUtils.MediaSizeComparator;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Activity for configuring a print job.
- */
-public class PrintJobConfigActivity extends Activity {
-
- private static final String LOG_TAG = "PrintJobConfigActivity";
-
- private static final boolean DEBUG = false;
-
- public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
-
- private static final int LOADER_ID_PRINTERS_LOADER = 1;
-
- private static final int ORIENTATION_PORTRAIT = 0;
- private static final int ORIENTATION_LANDSCAPE = 1;
-
- private static final int DEST_ADAPTER_MAX_ITEM_COUNT = 9;
-
- private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
- private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;
-
- private static final int ACTIVITY_REQUEST_CREATE_FILE = 1;
- private static final int ACTIVITY_REQUEST_SELECT_PRINTER = 2;
- private static final int ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS = 3;
-
- private static final int CONTROLLER_STATE_FINISHED = 1;
- private static final int CONTROLLER_STATE_FAILED = 2;
- private static final int CONTROLLER_STATE_CANCELLED = 3;
- private static final int CONTROLLER_STATE_INITIALIZED = 4;
- private static final int CONTROLLER_STATE_STARTED = 5;
- private static final int CONTROLLER_STATE_LAYOUT_STARTED = 6;
- private static final int CONTROLLER_STATE_LAYOUT_COMPLETED = 7;
- private static final int CONTROLLER_STATE_WRITE_STARTED = 8;
- private static final int CONTROLLER_STATE_WRITE_COMPLETED = 9;
-
- private static final int EDITOR_STATE_INITIALIZED = 1;
- private static final int EDITOR_STATE_CONFIRMED_PRINT = 2;
- private static final int EDITOR_STATE_CANCELLED = 3;
-
- private static final int MIN_COPIES = 1;
- private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES);
-
- private static final Pattern PATTERN_DIGITS = Pattern.compile("[\\d]+");
-
- private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
- "(?=[]\\[+&|!(){}^\"~*?:\\\\])");
-
- private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
- "[\\s]*[0-9]*[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*?(([,])"
- + "[\\s]*[0-9]*[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*|[\\s]*)+");
-
- public static final PageRange[] ALL_PAGES_ARRAY = new PageRange[] {PageRange.ALL_PAGES};
-
- private final PrintAttributes mOldPrintAttributes = new PrintAttributes.Builder().build();
- private final PrintAttributes mCurrPrintAttributes = new PrintAttributes.Builder().build();
-
- private final DeathRecipient mDeathRecipient = new DeathRecipient() {
- @Override
- public void binderDied() {
- finish();
- }
- };
-
- private Editor mEditor;
- private Document mDocument;
- private PrintController mController;
-
- private PrintJobId mPrintJobId;
-
- private IBinder mIPrintDocumentAdapter;
-
- private Dialog mGeneratingPrintJobDialog;
-
- private PrintSpoolerProvider mSpoolerProvider;
-
- private String mCallingPackageName;
-
- @Override
- protected void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- setTitle(R.string.print_dialog);
-
- Bundle extras = getIntent().getExtras();
-
- PrintJobInfo printJob = extras.getParcelable(PrintManager.EXTRA_PRINT_JOB);
- if (printJob == null) {
- throw new IllegalArgumentException("printJob cannot be null");
- }
-
- mPrintJobId = printJob.getId();
- mIPrintDocumentAdapter = extras.getBinder(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER);
- if (mIPrintDocumentAdapter == null) {
- throw new IllegalArgumentException("PrintDocumentAdapter cannot be null");
- }
-
- try {
- IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter)
- .setObserver(new PrintDocumentAdapterObserver(this));
- } catch (RemoteException re) {
- finish();
- return;
- }
-
- PrintAttributes attributes = printJob.getAttributes();
- if (attributes != null) {
- mCurrPrintAttributes.copyFrom(attributes);
- }
-
- mCallingPackageName = extras.getString(DocumentsContract.EXTRA_PACKAGE_NAME);
-
- setContentView(R.layout.print_job_config_activity_container);
-
- try {
- mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException re) {
- finish();
- return;
- }
-
- mDocument = new Document();
- mEditor = new Editor();
-
- mSpoolerProvider = new PrintSpoolerProvider(this,
- new Runnable() {
- @Override
- public void run() {
- // We got the spooler so unleash the UI.
- mController = new PrintController(new RemotePrintDocumentAdapter(
- IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
- mSpoolerProvider.getSpooler().generateFileForPrintJob(mPrintJobId)));
- mController.initialize();
-
- mEditor.initialize();
- mEditor.postCreate();
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if (mSpoolerProvider.getSpooler() != null) {
- mEditor.refreshCurrentPrinter();
- }
- }
-
- @Override
- public void onPause() {
- if (isFinishing()) {
- if (mController != null && mController.hasStarted()) {
- mController.finish();
- }
- if (mEditor != null && mEditor.isPrintConfirmed()
- && mController != null && mController.isFinished()) {
- mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_QUEUED, null);
- } else {
- mSpoolerProvider.getSpooler().setPrintJobState(mPrintJobId,
- PrintJobInfo.STATE_CANCELED, null);
- }
- if (mGeneratingPrintJobDialog != null) {
- mGeneratingPrintJobDialog.dismiss();
- mGeneratingPrintJobDialog = null;
- }
- mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
- mSpoolerProvider.destroy();
- }
- super.onPause();
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- if (mController != null && mEditor != null &&
- !mEditor.isPrintConfirmed() && mEditor.shouldCloseOnTouch(event)) {
- if (!mController.isWorking()) {
- PrintJobConfigActivity.this.finish();
- }
- mEditor.cancel();
- return true;
- }
- return super.onTouchEvent(event);
- }
-
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- event.startTracking();
- }
- return super.onKeyDown(keyCode, event);
- }
-
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (mController != null && mEditor != null) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- if (mEditor.isShwoingGeneratingPrintJobUi()) {
- return true;
- }
- if (event.isTracking() && !event.isCanceled()) {
- if (!mController.isWorking()) {
- PrintJobConfigActivity.this.finish();
- }
- }
- mEditor.cancel();
- return true;
- }
- }
- return super.onKeyUp(keyCode, event);
- }
-
- private boolean printAttributesChanged() {
- return !mOldPrintAttributes.equals(mCurrPrintAttributes);
- }
-
- private class PrintController {
- private final AtomicInteger mRequestCounter = new AtomicInteger();
-
- private final RemotePrintDocumentAdapter mRemotePrintAdapter;
-
- private final Bundle mMetadata;
-
- private final ControllerHandler mHandler;
-
- private final LayoutResultCallback mLayoutResultCallback;
-
- private final WriteResultCallback mWriteResultCallback;
-
- private int mControllerState = CONTROLLER_STATE_INITIALIZED;
-
- private boolean mHasStarted;
-
- private PageRange[] mRequestedPages;
-
- public PrintController(RemotePrintDocumentAdapter adapter) {
- mRemotePrintAdapter = adapter;
- mMetadata = new Bundle();
- mHandler = new ControllerHandler(getMainLooper());
- mLayoutResultCallback = new LayoutResultCallback(mHandler);
- mWriteResultCallback = new WriteResultCallback(mHandler);
- }
-
- public void initialize() {
- mHasStarted = false;
- mControllerState = CONTROLLER_STATE_INITIALIZED;
- }
-
- public void cancel() {
- if (isWorking()) {
- mRemotePrintAdapter.cancel();
- }
- mControllerState = CONTROLLER_STATE_CANCELLED;
- }
-
- public boolean isCancelled() {
- return (mControllerState == CONTROLLER_STATE_CANCELLED);
- }
-
- public boolean isFinished() {
- return (mControllerState == CONTROLLER_STATE_FINISHED);
- }
-
- public boolean hasStarted() {
- return mHasStarted;
- }
-
- public boolean hasPerformedLayout() {
- return mControllerState >= CONTROLLER_STATE_LAYOUT_COMPLETED;
- }
-
- public boolean isPerformingLayout() {
- return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED;
- }
-
- public boolean isWorking() {
- return mControllerState == CONTROLLER_STATE_LAYOUT_STARTED
- || mControllerState == CONTROLLER_STATE_WRITE_STARTED;
- }
-
- public void start() {
- mControllerState = CONTROLLER_STATE_STARTED;
- mHasStarted = true;
- mRemotePrintAdapter.start();
- }
-
- public void update() {
- if (!mController.hasStarted()) {
- mController.start();
- }
-
- // If the print attributes are the same and we are performing
- // a layout, then we have to wait for it to completed which will
- // trigger writing of the necessary pages.
- final boolean printAttributesChanged = printAttributesChanged();
- if (!printAttributesChanged && isPerformingLayout()) {
- return;
- }
-
- // If print is confirmed we always do a layout since the previous
- // ones were for preview and this one is for printing.
- if (!printAttributesChanged && !mEditor.isPrintConfirmed()) {
- if (mDocument.info == null) {
- // We are waiting for the result of a layout, so do nothing.
- return;
- }
- // If the attributes didn't change and we have done a layout, then
- // we do not do a layout but may have to ask the app to write some
- // pages. Hence, pretend layout completed and nothing changed, so
- // we handle writing as usual.
- handleOnLayoutFinished(mDocument.info, false, mRequestCounter.get());
- } else {
- mSpoolerProvider.getSpooler().setPrintJobAttributesNoPersistence(
- mPrintJobId, mCurrPrintAttributes);
-
- mMetadata.putBoolean(PrintDocumentAdapter.EXTRA_PRINT_PREVIEW,
- !mEditor.isPrintConfirmed());
-
- mControllerState = CONTROLLER_STATE_LAYOUT_STARTED;
-
- mRemotePrintAdapter.layout(mOldPrintAttributes, mCurrPrintAttributes,
- mLayoutResultCallback, mMetadata, mRequestCounter.incrementAndGet());
-
- mOldPrintAttributes.copyFrom(mCurrPrintAttributes);
- }
- }
-
- public void finish() {
- mControllerState = CONTROLLER_STATE_FINISHED;
- mRemotePrintAdapter.finish();
- }
-
- private void handleOnLayoutFinished(PrintDocumentInfo info,
- boolean layoutChanged, int sequence) {
- if (mRequestCounter.get() != sequence) {
- return;
- }
-
- if (isCancelled()) {
- mEditor.updateUi();
- if (mEditor.isDone()) {
- PrintJobConfigActivity.this.finish();
- }
- return;
- }
-
- final int oldControllerState = mControllerState;
-
- if (mControllerState == CONTROLLER_STATE_LAYOUT_STARTED) {
- mControllerState = CONTROLLER_STATE_LAYOUT_COMPLETED;
- }
-
- // For layout purposes we care only whether the type or the page
- // count changed. We still do not have the size since we did not
- // call write. We use "layoutChanged" set by the application to
- // know whether something else changed about the document.
- final boolean infoChanged = !equalsIgnoreSize(info, mDocument.info);
- // If the info changed, we update the document and the print job.
- if (infoChanged) {
- mDocument.info = info;
- // Set the info.
- mSpoolerProvider.getSpooler().setPrintJobPrintDocumentInfoNoPersistence(
- mPrintJobId, info);
- }
-
- // If the document info or the layout changed, then
- // drop the pages since we have to fetch them again.
- if (infoChanged || layoutChanged) {
- mDocument.pages = null;
- mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(
- mPrintJobId, null);
- }
-
- PageRange[] oldRequestedPages = mRequestedPages;
-
- // No pages means that the user selected an invalid range while we
- // were doing a layout or the layout returned a document info for
- // which the selected range is invalid. In such a case we do not
- // write anything and wait for the user to fix the range which will
- // trigger an update.
- mRequestedPages = mEditor.getRequestedPages();
- if (mRequestedPages == null || mRequestedPages.length == 0) {
- mEditor.updateUi();
- if (mEditor.isDone()) {
- PrintJobConfigActivity.this.finish();
- }
- return;
- } else {
- // If print is not confirmed we just ask for the first of the
- // selected pages to emulate a behavior that shows preview
- // increasing the chances that apps will implement the APIs
- // correctly.
- if (!mEditor.isPrintConfirmed()) {
- if (ALL_PAGES_ARRAY.equals(mRequestedPages)) {
- mRequestedPages = new PageRange[] {new PageRange(0, 0)};
- } else {
- final int firstPage = mRequestedPages[0].getStart();
- mRequestedPages = new PageRange[] {new PageRange(firstPage, firstPage)};
- }
- }
- }
-
- // If the info and the layout did not change...
- if (!infoChanged && !layoutChanged
- // and we have the requested pages ...
- && (PageRangeUtils.contains(mDocument.pages, mRequestedPages))
- // ...or the requested pages are being written...
- || (oldControllerState == CONTROLLER_STATE_WRITE_STARTED
- && oldRequestedPages != null
- && PageRangeUtils.contains(oldRequestedPages, mRequestedPages))) {
- // Nothing interesting changed and we have all requested pages.
- // Then update the print jobs's pages as we will not do a write
- // and we usually update the pages in the write complete callback.
- if (mDocument.pages != null) {
- // Update the print job's pages given we have them.
- updatePrintJobPages(mDocument.pages, mRequestedPages);
- }
- mEditor.updateUi();
- if (mEditor.isDone()) {
- requestCreatePdfFileOrFinish();
- }
- return;
- }
-
- mEditor.updateUi();
-
- // Request a write of the pages of interest.
- mControllerState = CONTROLLER_STATE_WRITE_STARTED;
- mRemotePrintAdapter.write(mRequestedPages, mWriteResultCallback,
- mRequestCounter.incrementAndGet());
- }
-
- private void handleOnLayoutFailed(final CharSequence error, int sequence) {
- if (mRequestCounter.get() != sequence) {
- return;
- }
- mControllerState = CONTROLLER_STATE_FAILED;
- mEditor.showUi(Editor.UI_ERROR, new Runnable() {
- @Override
- public void run() {
- if (!TextUtils.isEmpty(error)) {
- TextView messageView = (TextView) findViewById(R.id.message);
- messageView.setText(error);
- }
- }
- });
- }
-
- private void handleOnWriteFinished(PageRange[] pages, int sequence) {
- if (mRequestCounter.get() != sequence) {
- return;
- }
-
- if (isCancelled()) {
- if (mEditor.isDone()) {
- PrintJobConfigActivity.this.finish();
- }
- return;
- }
-
- mControllerState = CONTROLLER_STATE_WRITE_COMPLETED;
-
- // Update the document size.
- File file = mSpoolerProvider.getSpooler()
- .generateFileForPrintJob(mPrintJobId);
- mDocument.info.setDataSize(file.length());
-
- // Update the print job with the updated info.
- mSpoolerProvider.getSpooler().setPrintJobPrintDocumentInfoNoPersistence(
- mPrintJobId, mDocument.info);
-
- // Update which pages we have fetched.
- mDocument.pages = PageRangeUtils.normalize(pages);
-
- if (DEBUG) {
- Log.i(LOG_TAG, "Requested: " + Arrays.toString(mRequestedPages)
- + " and got: " + Arrays.toString(mDocument.pages));
- }
-
- updatePrintJobPages(mDocument.pages, mRequestedPages);
-
- if (mEditor.isDone()) {
- requestCreatePdfFileOrFinish();
- }
- }
-
- private void updatePrintJobPages(PageRange[] writtenPages, PageRange[] requestedPages) {
- // Adjust the print job pages based on what was requested and written.
- // The cases are ordered in the most expected to the least expected.
- if (Arrays.equals(writtenPages, requestedPages)) {
- // We got a document with exactly the pages we wanted. Hence,
- // the printer has to print all pages in the data.
- mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
- ALL_PAGES_ARRAY);
- } else if (Arrays.equals(writtenPages, ALL_PAGES_ARRAY)) {
- // We requested specific pages but got all of them. Hence,
- // the printer has to print only the requested pages.
- mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
- requestedPages);
- } else if (PageRangeUtils.contains(writtenPages, requestedPages)) {
- // We requested specific pages and got more but not all pages.
- // Hence, we have to offset appropriately the printed pages to
- // be based off the start of the written ones instead of zero.
- // The written pages are always non-null and not empty.
- final int offset = -writtenPages[0].getStart();
- PageRange[] offsetPages = Arrays.copyOf(requestedPages, requestedPages.length);
- PageRangeUtils.offset(offsetPages, offset);
- mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
- offsetPages);
- } else if (Arrays.equals(requestedPages, ALL_PAGES_ARRAY)
- && writtenPages.length == 1 && writtenPages[0].getStart() == 0
- && writtenPages[0].getEnd() == mDocument.info.getPageCount() - 1) {
- // We requested all pages via the special constant and got all
- // of them as an explicit enumeration. Hence, the printer has
- // to print only the requested pages.
- mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(mPrintJobId,
- writtenPages);
- } else {
- // We did not get the pages we requested, then the application
- // misbehaves, so we fail quickly.
- mControllerState = CONTROLLER_STATE_FAILED;
- Log.e(LOG_TAG, "Received invalid pages from the app: requested="
- + Arrays.toString(requestedPages) + " written="
- + Arrays.toString(writtenPages));
- mEditor.showUi(Editor.UI_ERROR, null);
- }
- }
-
- private void requestCreatePdfFileOrFinish() {
- if (mEditor.isPrintingToPdf()) {
- Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
- intent.setType("application/pdf");
- intent.putExtra(Intent.EXTRA_TITLE, mDocument.info.getName());
- intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, mCallingPackageName);
- startActivityForResult(intent, ACTIVITY_REQUEST_CREATE_FILE);
- } else {
- PrintJobConfigActivity.this.finish();
- }
- }
-
- private void handleOnWriteFailed(final CharSequence error, int sequence) {
- if (mRequestCounter.get() != sequence) {
- return;
- }
- mControllerState = CONTROLLER_STATE_FAILED;
- mEditor.showUi(Editor.UI_ERROR, new Runnable() {
- @Override
- public void run() {
- if (!TextUtils.isEmpty(error)) {
- TextView messageView = (TextView) findViewById(R.id.message);
- messageView.setText(error);
- }
- }
- });
- }
-
- private boolean equalsIgnoreSize(PrintDocumentInfo lhs, PrintDocumentInfo rhs) {
- if (lhs == rhs) {
- return true;
- }
- if (lhs == null) {
- if (rhs != null) {
- return false;
- }
- } else {
- if (rhs == null) {
- return false;
- }
- if (lhs.getContentType() != rhs.getContentType()
- || lhs.getPageCount() != rhs.getPageCount()) {
- return false;
- }
- }
- return true;
- }
-
- private final class ControllerHandler extends Handler {
- public static final int MSG_ON_LAYOUT_FINISHED = 1;
- public static final int MSG_ON_LAYOUT_FAILED = 2;
- public static final int MSG_ON_WRITE_FINISHED = 3;
- public static final int MSG_ON_WRITE_FAILED = 4;
-
- public ControllerHandler(Looper looper) {
- super(looper, null, false);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_ON_LAYOUT_FINISHED: {
- PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
- final boolean changed = (message.arg1 == 1);
- final int sequence = message.arg2;
- handleOnLayoutFinished(info, changed, sequence);
- } break;
-
- case MSG_ON_LAYOUT_FAILED: {
- CharSequence error = (CharSequence) message.obj;
- final int sequence = message.arg1;
- handleOnLayoutFailed(error, sequence);
- } break;
-
- case MSG_ON_WRITE_FINISHED: {
- PageRange[] pages = (PageRange[]) message.obj;
- final int sequence = message.arg1;
- handleOnWriteFinished(pages, sequence);
- } break;
-
- case MSG_ON_WRITE_FAILED: {
- CharSequence error = (CharSequence) message.obj;
- final int sequence = message.arg1;
- handleOnWriteFailed(error, sequence);
- } break;
- }
- }
- }
- }
-
- private static final class LayoutResultCallback extends ILayoutResultCallback.Stub {
- private final WeakReference<PrintController.ControllerHandler> mWeakHandler;
-
- public LayoutResultCallback(PrintController.ControllerHandler handler) {
- mWeakHandler = new WeakReference<PrintController.ControllerHandler>(handler);
- }
-
- @Override
- public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) {
- Handler handler = mWeakHandler.get();
- if (handler != null) {
- handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FINISHED,
- changed ? 1 : 0, sequence, info).sendToTarget();
- }
- }
-
- @Override
- public void onLayoutFailed(CharSequence error, int sequence) {
- Handler handler = mWeakHandler.get();
- if (handler != null) {
- handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_LAYOUT_FAILED,
- sequence, 0, error).sendToTarget();
- }
- }
- }
-
- private static final class WriteResultCallback extends IWriteResultCallback.Stub {
- private final WeakReference<PrintController.ControllerHandler> mWeakHandler;
-
- public WriteResultCallback(PrintController.ControllerHandler handler) {
- mWeakHandler = new WeakReference<PrintController.ControllerHandler>(handler);
- }
-
- @Override
- public void onWriteFinished(PageRange[] pages, int sequence) {
- Handler handler = mWeakHandler.get();
- if (handler != null) {
- handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FINISHED,
- sequence, 0, pages).sendToTarget();
- }
- }
-
- @Override
- public void onWriteFailed(CharSequence error, int sequence) {
- Handler handler = mWeakHandler.get();
- if (handler != null) {
- handler.obtainMessage(PrintController.ControllerHandler.MSG_ON_WRITE_FAILED,
- sequence, 0, error).sendToTarget();
- }
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case ACTIVITY_REQUEST_CREATE_FILE: {
- if (data != null) {
- Uri uri = data.getData();
- writePrintJobDataAndFinish(uri);
- } else {
- mEditor.showUi(Editor.UI_EDITING_PRINT_JOB,
- new Runnable() {
- @Override
- public void run() {
- mEditor.initialize();
- mEditor.bindUi();
- mEditor.reselectCurrentPrinter();
- mEditor.updateUi();
- }
- });
- }
- } break;
-
- case ACTIVITY_REQUEST_SELECT_PRINTER: {
- if (resultCode == RESULT_OK) {
- PrinterId printerId = (PrinterId) data.getParcelableExtra(
- INTENT_EXTRA_PRINTER_ID);
- if (printerId != null) {
- mEditor.ensurePrinterSelected(printerId);
- break;
- }
- }
- mEditor.ensureCurrentPrinterSelected();
- } break;
-
- case ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS: {
- if (resultCode == RESULT_OK) {
- PrintJobInfo printJobInfo = (PrintJobInfo) data.getParcelableExtra(
- PrintService.EXTRA_PRINT_JOB_INFO);
- if (printJobInfo != null) {
- mEditor.updateFromAdvancedOptions(printJobInfo);
- break;
- }
- }
- mEditor.cancel();
- PrintJobConfigActivity.this.finish();
- } break;
- }
- }
-
- private void writePrintJobDataAndFinish(final Uri uri) {
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- InputStream in = null;
- OutputStream out = null;
- try {
- PrintJobInfo printJob = mSpoolerProvider.getSpooler()
- .getPrintJobInfo(mPrintJobId, PrintManager.APP_ID_ANY);
- if (printJob == null) {
- return null;
- }
- File file = mSpoolerProvider.getSpooler()
- .generateFileForPrintJob(mPrintJobId);
- in = new FileInputStream(file);
- out = getContentResolver().openOutputStream(uri);
- final byte[] buffer = new byte[8192];
- while (true) {
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- break;
- }
- out.write(buffer, 0, readByteCount);
- }
- } catch (FileNotFoundException fnfe) {
- Log.e(LOG_TAG, "Error writing print job data!", fnfe);
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error writing print job data!", ioe);
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
- }
- return null;
- }
-
- @Override
- public void onPostExecute(Void result) {
- mEditor.cancel();
- PrintJobConfigActivity.this.finish();
- }
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
- }
-
- private final class Editor {
- private static final int UI_NONE = 0;
- private static final int UI_EDITING_PRINT_JOB = 1;
- private static final int UI_GENERATING_PRINT_JOB = 2;
- private static final int UI_ERROR = 3;
-
- private EditText mCopiesEditText;
-
- private TextView mRangeOptionsTitle;
- private TextView mPageRangeTitle;
- private EditText mPageRangeEditText;
-
- private Spinner mDestinationSpinner;
- private DestinationAdapter mDestinationSpinnerAdapter;
-
- private Spinner mMediaSizeSpinner;
- private ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
-
- private Spinner mColorModeSpinner;
- private ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
-
- private Spinner mOrientationSpinner;
- private ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
-
- private Spinner mRangeOptionsSpinner;
- private ArrayAdapter<SpinnerItem<Integer>> mRangeOptionsSpinnerAdapter;
-
- private SimpleStringSplitter mStringCommaSplitter =
- new SimpleStringSplitter(',');
-
- private View mContentContainer;
-
- private View mAdvancedPrintOptionsContainer;
-
- private Button mAdvancedOptionsButton;
-
- private Button mPrintButton;
-
- private PrinterId mNextPrinterId;
-
- private PrinterInfo mCurrentPrinter;
-
- private MediaSizeComparator mMediaSizeComparator;
-
- private final OnFocusChangeListener mFocusListener = new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- EditText editText = (EditText) view;
- if (!TextUtils.isEmpty(editText.getText())) {
- editText.setSelection(editText.getText().length());
- }
- }
- };
-
- private final OnItemSelectedListener mOnItemSelectedListener =
- new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {
- if (spinner == mDestinationSpinner) {
- if (mIgnoreNextDestinationChange) {
- mIgnoreNextDestinationChange = false;
- return;
- }
-
- if (position == AdapterView.INVALID_POSITION) {
- updateUi();
- return;
- }
-
- if (id == DEST_ADAPTER_ITEM_ID_ALL_PRINTERS) {
- startSelectPrinterActivity();
- return;
- }
-
- mCapabilitiesTimeout.remove();
-
- mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter
- .getItem(position);
-
- mSpoolerProvider.getSpooler().setPrintJobPrinterNoPersistence(
- mPrintJobId, mCurrentPrinter);
-
- if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE) {
- mCapabilitiesTimeout.post();
- updateUi();
- return;
- }
-
- PrinterCapabilitiesInfo capabilities = mCurrentPrinter.getCapabilities();
- if (capabilities == null) {
- mCapabilitiesTimeout.post();
- updateUi();
- refreshCurrentPrinter();
- } else {
- updatePrintAttributes(capabilities);
- updateUi();
- mController.update();
- refreshCurrentPrinter();
- }
- } else if (spinner == mMediaSizeSpinner) {
- if (mIgnoreNextMediaSizeChange) {
- mIgnoreNextMediaSizeChange = false;
- return;
- }
- if (mOldMediaSizeSelectionIndex
- == mMediaSizeSpinner.getSelectedItemPosition()) {
- mOldMediaSizeSelectionIndex = AdapterView.INVALID_POSITION;
- return;
- }
- SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
- if (mOrientationSpinner.getSelectedItemPosition() == 0) {
- mCurrPrintAttributes.setMediaSize(mediaItem.value.asPortrait());
- } else {
- mCurrPrintAttributes.setMediaSize(mediaItem.value.asLandscape());
- }
- if (!hasErrors()) {
- mController.update();
- }
- } else if (spinner == mColorModeSpinner) {
- if (mIgnoreNextColorChange) {
- mIgnoreNextColorChange = false;
- return;
- }
- if (mOldColorModeSelectionIndex
- == mColorModeSpinner.getSelectedItemPosition()) {
- mOldColorModeSelectionIndex = AdapterView.INVALID_POSITION;
- return;
- }
- SpinnerItem<Integer> colorModeItem =
- mColorModeSpinnerAdapter.getItem(position);
- mCurrPrintAttributes.setColorMode(colorModeItem.value);
- if (!hasErrors()) {
- mController.update();
- }
- } else if (spinner == mOrientationSpinner) {
- if (mIgnoreNextOrientationChange) {
- mIgnoreNextOrientationChange = false;
- return;
- }
- SpinnerItem<Integer> orientationItem =
- mOrientationSpinnerAdapter.getItem(position);
- setCurrentPrintAttributesOrientation(orientationItem.value);
- if (!hasErrors()) {
- mController.update();
- }
- } else if (spinner == mRangeOptionsSpinner) {
- if (mIgnoreNextRangeOptionChange) {
- mIgnoreNextRangeOptionChange = false;
- return;
- }
- updateUi();
- if (!hasErrors()) {
- mController.update();
- }
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- /* do nothing*/
- }
- };
-
- private void setCurrentPrintAttributesOrientation(int orientation) {
- MediaSize mediaSize = mCurrPrintAttributes.getMediaSize();
- if (orientation == ORIENTATION_PORTRAIT) {
- if (!mediaSize.isPortrait()) {
- // Rotate the media size.
- mCurrPrintAttributes.setMediaSize(mediaSize.asPortrait());
-
- // Rotate the resolution.
- Resolution oldResolution = mCurrPrintAttributes.getResolution();
- Resolution newResolution = new Resolution(
- oldResolution.getId(),
- oldResolution.getLabel(),
- oldResolution.getVerticalDpi(),
- oldResolution.getHorizontalDpi());
- mCurrPrintAttributes.setResolution(newResolution);
-
- // Rotate the physical margins.
- Margins oldMinMargins = mCurrPrintAttributes.getMinMargins();
- Margins newMinMargins = new Margins(
- oldMinMargins.getBottomMils(),
- oldMinMargins.getLeftMils(),
- oldMinMargins.getTopMils(),
- oldMinMargins.getRightMils());
- mCurrPrintAttributes.setMinMargins(newMinMargins);
- }
- } else {
- if (mediaSize.isPortrait()) {
- // Rotate the media size.
- mCurrPrintAttributes.setMediaSize(mediaSize.asLandscape());
-
- // Rotate the resolution.
- Resolution oldResolution = mCurrPrintAttributes.getResolution();
- Resolution newResolution = new Resolution(
- oldResolution.getId(),
- oldResolution.getLabel(),
- oldResolution.getVerticalDpi(),
- oldResolution.getHorizontalDpi());
- mCurrPrintAttributes.setResolution(newResolution);
-
- // Rotate the physical margins.
- Margins oldMinMargins = mCurrPrintAttributes.getMinMargins();
- Margins newMargins = new Margins(
- oldMinMargins.getTopMils(),
- oldMinMargins.getRightMils(),
- oldMinMargins.getBottomMils(),
- oldMinMargins.getLeftMils());
- mCurrPrintAttributes.setMinMargins(newMargins);
- }
- }
- }
-
- private void updatePrintAttributes(PrinterCapabilitiesInfo capabilities) {
- PrintAttributes defaults = capabilities.getDefaults();
-
- // Sort the media sizes based on the current locale.
- List<MediaSize> sortedMediaSizes = new ArrayList<MediaSize>(
- capabilities.getMediaSizes());
- Collections.sort(sortedMediaSizes, mMediaSizeComparator);
-
- // Media size.
- MediaSize currMediaSize = mCurrPrintAttributes.getMediaSize();
- if (currMediaSize == null) {
- mCurrPrintAttributes.setMediaSize(defaults.getMediaSize());
- } else {
- MediaSize currMediaSizePortrait = currMediaSize.asPortrait();
- final int mediaSizeCount = sortedMediaSizes.size();
- for (int i = 0; i < mediaSizeCount; i++) {
- MediaSize mediaSize = sortedMediaSizes.get(i);
- if (currMediaSizePortrait.equals(mediaSize.asPortrait())) {
- mCurrPrintAttributes.setMediaSize(currMediaSize);
- break;
- }
- }
- }
-
- // Color mode.
- final int colorMode = mCurrPrintAttributes.getColorMode();
- if ((capabilities.getColorModes() & colorMode) == 0) {
- mCurrPrintAttributes.setColorMode(colorMode);
- }
-
- // Resolution
- Resolution resolution = mCurrPrintAttributes.getResolution();
- if (resolution == null || !capabilities.getResolutions().contains(resolution)) {
- mCurrPrintAttributes.setResolution(defaults.getResolution());
- }
-
- // Margins.
- Margins margins = mCurrPrintAttributes.getMinMargins();
- if (margins == null) {
- mCurrPrintAttributes.setMinMargins(defaults.getMinMargins());
- } else {
- Margins minMargins = capabilities.getMinMargins();
- if (margins.getLeftMils() < minMargins.getLeftMils()
- || margins.getTopMils() < minMargins.getTopMils()
- || margins.getRightMils() > minMargins.getRightMils()
- || margins.getBottomMils() > minMargins.getBottomMils()) {
- mCurrPrintAttributes.setMinMargins(defaults.getMinMargins());
- }
- }
- }
-
- private final TextWatcher mCopiesTextWatcher = new TextWatcher() {
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- /* do nothing */
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- /* do nothing */
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- if (mIgnoreNextCopiesChange) {
- mIgnoreNextCopiesChange = false;
- return;
- }
-
- final boolean hadErrors = hasErrors();
-
- if (editable.length() == 0) {
- mCopiesEditText.setError("");
- updateUi();
- return;
- }
-
- int copies = 0;
- try {
- copies = Integer.parseInt(editable.toString());
- } catch (NumberFormatException nfe) {
- /* ignore */
- }
-
- if (copies < MIN_COPIES) {
- mCopiesEditText.setError("");
- updateUi();
- return;
- }
-
- mCopiesEditText.setError(null);
- mSpoolerProvider.getSpooler().setPrintJobCopiesNoPersistence(
- mPrintJobId, copies);
- updateUi();
-
- if (hadErrors && !hasErrors() && printAttributesChanged()) {
- mController.update();
- }
- }
- };
-
- private final TextWatcher mRangeTextWatcher = new TextWatcher() {
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- /* do nothing */
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- /* do nothing */
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- if (mIgnoreNextRangeChange) {
- mIgnoreNextRangeChange = false;
- return;
- }
-
- final boolean hadErrors = hasErrors();
-
- String text = editable.toString();
-
- if (TextUtils.isEmpty(text)) {
- mPageRangeEditText.setError("");
- updateUi();
- return;
- }
-
- String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
- if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
- mPageRangeEditText.setError("");
- updateUi();
- return;
- }
-
- // The range
- Matcher matcher = PATTERN_DIGITS.matcher(text);
- while (matcher.find()) {
- String numericString = text.substring(matcher.start(), matcher.end()).trim();
- if (TextUtils.isEmpty(numericString)) {
- continue;
- }
- final int pageIndex = Integer.parseInt(numericString);
- if (pageIndex < 1 || pageIndex > mDocument.info.getPageCount()) {
- mPageRangeEditText.setError("");
- updateUi();
- return;
- }
- }
-
- // We intentionally do not catch the case of the from page being
- // greater than the to page. When computing the requested pages
- // we just swap them if necessary.
-
- // Keep the print job up to date with the selected pages if we
- // know how many pages are there in the document.
- PageRange[] requestedPages = getRequestedPages();
- if (requestedPages != null && requestedPages.length > 0
- && requestedPages[requestedPages.length - 1].getEnd()
- < mDocument.info.getPageCount()) {
- mSpoolerProvider.getSpooler().setPrintJobPagesNoPersistence(
- mPrintJobId, requestedPages);
- }
-
- mPageRangeEditText.setError(null);
- mPrintButton.setEnabled(true);
- updateUi();
-
- if (hadErrors && !hasErrors() && printAttributesChanged()) {
- updateUi();
- }
- }
- };
-
- private final WaitForPrinterCapabilitiesTimeout mCapabilitiesTimeout =
- new WaitForPrinterCapabilitiesTimeout();
-
- private int mEditorState;
-
- private boolean mIgnoreNextDestinationChange;
- private int mOldMediaSizeSelectionIndex;
- private int mOldColorModeSelectionIndex;
- private boolean mIgnoreNextOrientationChange;
- private boolean mIgnoreNextRangeOptionChange;
- private boolean mIgnoreNextCopiesChange;
- private boolean mIgnoreNextRangeChange;
- private boolean mIgnoreNextMediaSizeChange;
- private boolean mIgnoreNextColorChange;
-
- private int mCurrentUi = UI_NONE;
-
- private boolean mFavoritePrinterSelected;
-
- public Editor() {
- showUi(UI_EDITING_PRINT_JOB, null);
- }
-
- public void postCreate() {
- // Destination.
- mMediaSizeComparator = new MediaSizeComparator(PrintJobConfigActivity.this);
- mDestinationSpinnerAdapter = new DestinationAdapter();
- mDestinationSpinnerAdapter.registerDataSetObserver(new DataSetObserver() {
- @Override
- public void onChanged() {
- // Initially, we have only safe to PDF as a printer but after some
- // printers are loaded we want to select the user's favorite one
- // which is the first.
- if (!mFavoritePrinterSelected && mDestinationSpinnerAdapter.getCount() > 1) {
- mFavoritePrinterSelected = true;
- mDestinationSpinner.setSelection(0);
- // Workaround again the weird spinner behavior to notify for selection
- // change on the next layout pass as the current printer is used below.
- mCurrentPrinter = (PrinterInfo) mDestinationSpinnerAdapter.getItem(0);
- }
-
- // If there is a next printer to select and we succeed selecting
- // it - done. Let the selection handling code make everything right.
- if (mNextPrinterId != null && selectPrinter(mNextPrinterId)) {
- mNextPrinterId = null;
- return;
- }
-
- // If the current printer properties changed, we update the UI.
- if (mCurrentPrinter != null) {
- final int printerCount = mDestinationSpinnerAdapter.getCount();
- for (int i = 0; i < printerCount; i++) {
- Object item = mDestinationSpinnerAdapter.getItem(i);
- // Some items are not printers
- if (item instanceof PrinterInfo) {
- PrinterInfo printer = (PrinterInfo) item;
- if (!printer.getId().equals(mCurrentPrinter.getId())) {
- continue;
- }
-
- // If nothing changed - done.
- if (mCurrentPrinter.equals(printer)) {
- return;
- }
-
- // If the current printer became available and has no
- // capabilities, we refresh it.
- if (mCurrentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE
- && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE
- && printer.getCapabilities() == null) {
- if (!mCapabilitiesTimeout.isPosted()) {
- mCapabilitiesTimeout.post();
- }
- mCurrentPrinter.copyFrom(printer);
- refreshCurrentPrinter();
- return;
- }
-
- // If the current printer became unavailable or its
- // capabilities go away, we update the UI and add a
- // timeout to declare the printer as unavailable.
- if ((mCurrentPrinter.getStatus() != PrinterInfo.STATUS_UNAVAILABLE
- && printer.getStatus() == PrinterInfo.STATUS_UNAVAILABLE)
- || (mCurrentPrinter.getCapabilities() != null
- && printer.getCapabilities() == null)) {
- if (!mCapabilitiesTimeout.isPosted()) {
- mCapabilitiesTimeout.post();
- }
- mCurrentPrinter.copyFrom(printer);
- updateUi();
- return;
- }
-
- // We just refreshed the current printer.
- if (printer.getCapabilities() != null
- && mCapabilitiesTimeout.isPosted()) {
- mCapabilitiesTimeout.remove();
- updatePrintAttributes(printer.getCapabilities());
- updateUi();
- mController.update();
- }
-
- // Update the UI if capabilities changed.
- boolean capabilitiesChanged = false;
-
- if (mCurrentPrinter.getCapabilities() == null) {
- if (printer.getCapabilities() != null) {
- updatePrintAttributes(printer.getCapabilities());
- capabilitiesChanged = true;
- }
- } else if (!mCurrentPrinter.getCapabilities().equals(
- printer.getCapabilities())) {
- capabilitiesChanged = true;
- }
-
- // Update the UI if the status changed.
- final boolean statusChanged = mCurrentPrinter.getStatus()
- != printer.getStatus();
-
- // Update the printer with the latest info.
- if (!mCurrentPrinter.equals(printer)) {
- mCurrentPrinter.copyFrom(printer);
- }
-
- if (capabilitiesChanged || statusChanged) {
- // If something changed during update...
- if (updateUi() || !mController.hasPerformedLayout()) {
- // Update the document.
- mController.update();
- }
- }
-
- break;
- }
- }
- }
- }
-
- @Override
- public void onInvalidated() {
- /* do nothing - we always have one fake PDF printer */
- }
- });
-
- // Media size.
- mMediaSizeSpinnerAdapter = new ArrayAdapter<SpinnerItem<MediaSize>>(
- PrintJobConfigActivity.this,
- R.layout.spinner_dropdown_item, R.id.title);
-
- // Color mode.
- mColorModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(
- PrintJobConfigActivity.this,
- R.layout.spinner_dropdown_item, R.id.title);
-
- // Orientation
- mOrientationSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(
- PrintJobConfigActivity.this,
- R.layout.spinner_dropdown_item, R.id.title);
- String[] orientationLabels = getResources().getStringArray(
- R.array.orientation_labels);
- mOrientationSpinnerAdapter.add(new SpinnerItem<Integer>(
- ORIENTATION_PORTRAIT, orientationLabels[0]));
- mOrientationSpinnerAdapter.add(new SpinnerItem<Integer>(
- ORIENTATION_LANDSCAPE, orientationLabels[1]));
-
- // Range options
- mRangeOptionsSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(
- PrintJobConfigActivity.this,
- R.layout.spinner_dropdown_item, R.id.title);
- final int[] rangeOptionsValues = getResources().getIntArray(
- R.array.page_options_values);
- String[] rangeOptionsLabels = getResources().getStringArray(
- R.array.page_options_labels);
- final int rangeOptionsCount = rangeOptionsLabels.length;
- for (int i = 0; i < rangeOptionsCount; i++) {
- mRangeOptionsSpinnerAdapter.add(new SpinnerItem<Integer>(
- rangeOptionsValues[i], rangeOptionsLabels[i]));
- }
-
- showUi(UI_EDITING_PRINT_JOB, null);
- bindUi();
- updateUi();
- }
-
- public void reselectCurrentPrinter() {
- if (mCurrentPrinter != null) {
- // TODO: While the data did not change and we set the adapter
- // to a newly inflated spinner, the latter does not show the
- // current item unless we poke the adapter. This requires more
- // investigation. Maybe an optimization in AdapterView does not
- // call into the adapter if the view is not visible which is the
- // case when we set the adapter.
- mDestinationSpinnerAdapter.notifyDataSetChanged();
- final int position = mDestinationSpinnerAdapter.getPrinterIndex(
- mCurrentPrinter.getId());
- mDestinationSpinner.setSelection(position);
- }
- }
-
- public void refreshCurrentPrinter() {
- PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
- if (printer != null) {
- FusedPrintersProvider printersLoader = (FusedPrintersProvider)
- (Loader<?>) getLoaderManager().getLoader(
- LOADER_ID_PRINTERS_LOADER);
- if (printersLoader != null) {
- printersLoader.setTrackedPrinter(printer.getId());
- }
- }
- }
-
- public void addCurrentPrinterToHistory() {
- PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
- PrinterId fakePdfPritnerId = mDestinationSpinnerAdapter.mFakePdfPrinter.getId();
- if (printer != null && !printer.getId().equals(fakePdfPritnerId)) {
- FusedPrintersProvider printersLoader = (FusedPrintersProvider)
- (Loader<?>) getLoaderManager().getLoader(
- LOADER_ID_PRINTERS_LOADER);
- if (printersLoader != null) {
- printersLoader.addHistoricalPrinter(printer);
- }
- }
- }
-
- public void updateFromAdvancedOptions(PrintJobInfo printJobInfo) {
- boolean updateContent = false;
-
- // Copies.
- mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
-
- // Media size and orientation
- PrintAttributes attributes = printJobInfo.getAttributes();
- if (!mCurrPrintAttributes.getMediaSize().equals(attributes.getMediaSize())) {
- final int mediaSizeCount = mMediaSizeSpinnerAdapter.getCount();
- for (int i = 0; i < mediaSizeCount; i++) {
- MediaSize mediaSize = mMediaSizeSpinnerAdapter.getItem(i).value;
- if (mediaSize.asPortrait().equals(attributes.getMediaSize().asPortrait())) {
- updateContent = true;
- mCurrPrintAttributes.setMediaSize(attributes.getMediaSize());
- mMediaSizeSpinner.setSelection(i);
- mIgnoreNextMediaSizeChange = true;
- if (attributes.getMediaSize().isPortrait()) {
- mOrientationSpinner.setSelection(0);
- mIgnoreNextOrientationChange = true;
- } else {
- mOrientationSpinner.setSelection(1);
- mIgnoreNextOrientationChange = true;
- }
- break;
- }
- }
- }
-
- // Color mode.
- final int colorMode = attributes.getColorMode();
- if (mCurrPrintAttributes.getColorMode() != colorMode) {
- if (colorMode == PrintAttributes.COLOR_MODE_MONOCHROME) {
- updateContent = true;
- mColorModeSpinner.setSelection(0);
- mIgnoreNextColorChange = true;
- mCurrPrintAttributes.setColorMode(attributes.getColorMode());
- } else if (colorMode == PrintAttributes.COLOR_MODE_COLOR) {
- updateContent = true;
- mColorModeSpinner.setSelection(1);
- mIgnoreNextColorChange = true;
- mCurrPrintAttributes.setColorMode(attributes.getColorMode());
- }
- }
-
- // Range.
- PageRange[] pageRanges = printJobInfo.getPages();
- if (pageRanges != null && pageRanges.length > 0) {
- pageRanges = PageRangeUtils.normalize(pageRanges);
- final int pageRangeCount = pageRanges.length;
- if (pageRangeCount == 1 && pageRanges[0] == PageRange.ALL_PAGES) {
- mRangeOptionsSpinner.setSelection(0);
- } else {
- final int pageCount = mDocument.info.getPageCount();
- if (pageRanges[0].getStart() >= 0
- && pageRanges[pageRanges.length - 1].getEnd() < pageCount) {
- mRangeOptionsSpinner.setSelection(1);
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < pageRangeCount; i++) {
- if (builder.length() > 0) {
- builder.append(',');
- }
- PageRange pageRange = pageRanges[i];
- final int shownStartPage = pageRange.getStart() + 1;
- final int shownEndPage = pageRange.getEnd() + 1;
- builder.append(shownStartPage);
- if (shownStartPage != shownEndPage) {
- builder.append('-');
- builder.append(shownEndPage);
- }
- }
- mPageRangeEditText.setText(builder.toString());
- }
- }
- }
-
- // Update the advanced options.
- mSpoolerProvider.getSpooler().setPrintJobAdvancedOptionsNoPersistence(
- mPrintJobId, printJobInfo.getAdvancedOptions());
-
- // Update the content if needed.
- if (updateContent) {
- mController.update();
- }
- }
-
- public void ensurePrinterSelected(PrinterId printerId) {
- // If the printer is not present maybe the loader is not
- // updated yet. In this case make a note and as soon as
- // the printer appears will will select it.
- if (!selectPrinter(printerId)) {
- mNextPrinterId = printerId;
- }
- }
-
- public boolean selectPrinter(PrinterId printerId) {
- mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerId);
- final int position = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
- if (position != AdapterView.INVALID_POSITION
- && position != mDestinationSpinner.getSelectedItemPosition()) {
- Object item = mDestinationSpinnerAdapter.getItem(position);
- mCurrentPrinter = (PrinterInfo) item;
- mDestinationSpinner.setSelection(position);
- return true;
- }
- return false;
- }
-
- public void ensureCurrentPrinterSelected() {
- if (mCurrentPrinter != null) {
- selectPrinter(mCurrentPrinter.getId());
- }
- }
-
- public boolean isPrintingToPdf() {
- return mDestinationSpinner.getSelectedItem()
- == mDestinationSpinnerAdapter.mFakePdfPrinter;
- }
-
- public boolean shouldCloseOnTouch(MotionEvent event) {
- if (event.getAction() != MotionEvent.ACTION_DOWN) {
- return false;
- }
-
- final int[] locationInWindow = new int[2];
- mContentContainer.getLocationInWindow(locationInWindow);
-
- final int windowTouchSlop = ViewConfiguration.get(PrintJobConfigActivity.this)
- .getScaledWindowTouchSlop();
- final int eventX = (int) event.getX();
- final int eventY = (int) event.getY();
- final int lenientWindowLeft = locationInWindow[0] - windowTouchSlop;
- final int lenientWindowRight = lenientWindowLeft + mContentContainer.getWidth()
- + windowTouchSlop;
- final int lenientWindowTop = locationInWindow[1] - windowTouchSlop;
- final int lenientWindowBottom = lenientWindowTop + mContentContainer.getHeight()
- + windowTouchSlop;
-
- if (eventX < lenientWindowLeft || eventX > lenientWindowRight
- || eventY < lenientWindowTop || eventY > lenientWindowBottom) {
- return true;
- }
- return false;
- }
-
- public boolean isShwoingGeneratingPrintJobUi() {
- return (mCurrentUi == UI_GENERATING_PRINT_JOB);
- }
-
- public void showUi(int ui, final Runnable postSwitchCallback) {
- if (ui == UI_NONE) {
- throw new IllegalStateException("cannot remove the ui");
- }
-
- if (mCurrentUi == ui) {
- return;
- }
-
- final int oldUi = mCurrentUi;
- mCurrentUi = ui;
-
- switch (oldUi) {
- case UI_NONE: {
- switch (ui) {
- case UI_EDITING_PRINT_JOB: {
- doUiSwitch(R.layout.print_job_config_activity_content_editing);
- registerPrintButtonClickListener();
- if (postSwitchCallback != null) {
- postSwitchCallback.run();
- }
- } break;
-
- case UI_GENERATING_PRINT_JOB: {
- doUiSwitch(R.layout.print_job_config_activity_content_generating);
- registerCancelButtonClickListener();
- if (postSwitchCallback != null) {
- postSwitchCallback.run();
- }
- } break;
- }
- } break;
-
- case UI_EDITING_PRINT_JOB: {
- switch (ui) {
- case UI_GENERATING_PRINT_JOB: {
- animateUiSwitch(R.layout.print_job_config_activity_content_generating,
- new Runnable() {
- @Override
- public void run() {
- registerCancelButtonClickListener();
- if (postSwitchCallback != null) {
- postSwitchCallback.run();
- }
- }
- },
- new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
- } break;
-
- case UI_ERROR: {
- animateUiSwitch(R.layout.print_job_config_activity_content_error,
- new Runnable() {
- @Override
- public void run() {
- registerOkButtonClickListener();
- if (postSwitchCallback != null) {
- postSwitchCallback.run();
- }
- }
- },
- new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
- } break;
- }
- } break;
-
- case UI_GENERATING_PRINT_JOB: {
- switch (ui) {
- case UI_EDITING_PRINT_JOB: {
- animateUiSwitch(R.layout.print_job_config_activity_content_editing,
- new Runnable() {
- @Override
- public void run() {
- registerPrintButtonClickListener();
- if (postSwitchCallback != null) {
- postSwitchCallback.run();
- }
- }
- },
- new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
- } break;
-
- case UI_ERROR: {
- animateUiSwitch(R.layout.print_job_config_activity_content_error,
- new Runnable() {
- @Override
- public void run() {
- registerOkButtonClickListener();
- if (postSwitchCallback != null) {
- postSwitchCallback.run();
- }
- }
- },
- new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
- } break;
- }
- } break;
-
- case UI_ERROR: {
- switch (ui) {
- case UI_EDITING_PRINT_JOB: {
- animateUiSwitch(R.layout.print_job_config_activity_content_editing,
- new Runnable() {
- @Override
- public void run() {
- registerPrintButtonClickListener();
- if (postSwitchCallback != null) {
- postSwitchCallback.run();
- }
- }
- },
- new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER));
- } break;
- }
- } break;
- }
- }
-
- private void registerAdvancedPrintOptionsButtonClickListener() {
- Button advancedOptionsButton = (Button) findViewById(R.id.advanced_settings_button);
- advancedOptionsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
- String activityName = getAdvancedOptionsActivityName(serviceName);
- if (TextUtils.isEmpty(activityName)) {
- return;
- }
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setComponent(new ComponentName(serviceName.getPackageName(),
- activityName));
-
- List<ResolveInfo> resolvedActivities = getPackageManager()
- .queryIntentActivities(intent, 0);
- if (resolvedActivities.isEmpty()) {
- return;
- }
- // The activity is a component name, therefore it is one or none.
- if (resolvedActivities.get(0).activityInfo.exported) {
- PrintJobInfo printJobInfo = mSpoolerProvider.getSpooler().getPrintJobInfo(
- mPrintJobId, PrintManager.APP_ID_ANY);
- intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, printJobInfo);
- // TODO: Make this an API for the next release.
- intent.putExtra("android.intent.extra.print.EXTRA_PRINTER_INFO",
- mCurrentPrinter);
- try {
- startActivityForResult(intent,
- ACTIVITY_POPULATE_ADVANCED_PRINT_OPTIONS);
- } catch (ActivityNotFoundException anfe) {
- Log.e(LOG_TAG, "Error starting activity for intent: " + intent, anfe);
- }
- }
- }
- });
- }
-
- private void registerPrintButtonClickListener() {
- Button printButton = (Button) findViewById(R.id.print_button);
- printButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
- if (printer != null) {
- mEditor.confirmPrint();
- mController.update();
- if (!printer.equals(mDestinationSpinnerAdapter.mFakePdfPrinter)) {
- mEditor.refreshCurrentPrinter();
- }
- } else {
- mEditor.cancel();
- PrintJobConfigActivity.this.finish();
- }
- }
- });
- }
-
- private void registerCancelButtonClickListener() {
- Button cancelButton = (Button) findViewById(R.id.cancel_button);
- cancelButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (!mController.isWorking()) {
- PrintJobConfigActivity.this.finish();
- }
- mEditor.cancel();
- }
- });
- }
-
- private void registerOkButtonClickListener() {
- Button okButton = (Button) findViewById(R.id.ok_button);
- okButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mEditor.showUi(Editor.UI_EDITING_PRINT_JOB, new Runnable() {
- @Override
- public void run() {
- // Start over with a clean slate.
- mOldPrintAttributes.clear();
- mController.initialize();
- mEditor.initialize();
- mEditor.bindUi();
- mEditor.reselectCurrentPrinter();
- if (!mController.hasPerformedLayout()) {
- mController.update();
- }
- }
- });
- }
- });
- }
-
- private void doUiSwitch(int showLayoutId) {
- ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
- contentContainer.removeAllViews();
- getLayoutInflater().inflate(showLayoutId, contentContainer, true);
- }
-
- private void animateUiSwitch(int showLayoutId, final Runnable beforeShowNewUiAction,
- final LayoutParams containerParams) {
- // Find everything we will shuffle around.
- final ViewGroup contentContainer = (ViewGroup) findViewById(R.id.content_container);
- final View hidingView = contentContainer.getChildAt(0);
- final View showingView = getLayoutInflater().inflate(showLayoutId,
- null, false);
-
- // First animation - fade out the old content.
- AutoCancellingAnimator.animate(hidingView).alpha(0.0f)
- .withLayer().withEndAction(new Runnable() {
- @Override
- public void run() {
- hidingView.setVisibility(View.INVISIBLE);
-
- // Prepare the new content with correct size and alpha.
- showingView.setMinimumWidth(contentContainer.getWidth());
- showingView.setAlpha(0.0f);
-
- // Compute how to much shrink /stretch the content.
- final int widthSpec = MeasureSpec.makeMeasureSpec(
- contentContainer.getWidth(), MeasureSpec.UNSPECIFIED);
- final int heightSpec = MeasureSpec.makeMeasureSpec(
- contentContainer.getHeight(), MeasureSpec.UNSPECIFIED);
- showingView.measure(widthSpec, heightSpec);
- final float scaleY = (float) showingView.getMeasuredHeight()
- / (float) contentContainer.getHeight();
-
- // Second animation - resize the container.
- AutoCancellingAnimator.animate(contentContainer).scaleY(scaleY)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- // Swap the old and the new content.
- contentContainer.removeAllViews();
- contentContainer.setScaleY(1.0f);
- contentContainer.addView(showingView);
-
- contentContainer.setLayoutParams(containerParams);
-
- beforeShowNewUiAction.run();
-
- // Third animation - show the new content.
- AutoCancellingAnimator.animate(showingView).alpha(1.0f);
- }
- });
- }
- });
- }
-
- public void initialize() {
- mEditorState = EDITOR_STATE_INITIALIZED;
- }
-
- public boolean isCancelled() {
- return mEditorState == EDITOR_STATE_CANCELLED;
- }
-
- public void cancel() {
- mEditorState = EDITOR_STATE_CANCELLED;
- mController.cancel();
- updateUi();
- }
-
- public boolean isDone() {
- return isPrintConfirmed() || isCancelled();
- }
-
- public boolean isPrintConfirmed() {
- return mEditorState == EDITOR_STATE_CONFIRMED_PRINT;
- }
-
- public void confirmPrint() {
- addCurrentPrinterToHistory();
- mEditorState = EDITOR_STATE_CONFIRMED_PRINT;
- showUi(UI_GENERATING_PRINT_JOB, null);
- }
-
- public PageRange[] getRequestedPages() {
- if (hasErrors()) {
- return null;
- }
- if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
- List<PageRange> pageRanges = new ArrayList<PageRange>();
- mStringCommaSplitter.setString(mPageRangeEditText.getText().toString());
-
- while (mStringCommaSplitter.hasNext()) {
- String range = mStringCommaSplitter.next().trim();
- if (TextUtils.isEmpty(range)) {
- continue;
- }
- final int dashIndex = range.indexOf('-');
- final int fromIndex;
- final int toIndex;
-
- if (dashIndex > 0) {
- fromIndex = Integer.parseInt(range.substring(0, dashIndex).trim()) - 1;
- // It is possible that the dash is at the end since the input
- // verification can has to allow the user to keep entering if
- // this would lead to a valid input. So we handle this.
- toIndex = (dashIndex < range.length() - 1)
- ? Integer.parseInt(range.substring(dashIndex + 1,
- range.length()).trim()) - 1 : fromIndex;
- } else {
- fromIndex = toIndex = Integer.parseInt(range) - 1;
- }
-
- PageRange pageRange = new PageRange(Math.min(fromIndex, toIndex),
- Math.max(fromIndex, toIndex));
- pageRanges.add(pageRange);
- }
-
- PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
- pageRanges.toArray(pageRangesArray);
-
- return PageRangeUtils.normalize(pageRangesArray);
- }
-
- return ALL_PAGES_ARRAY;
- }
-
- private void bindUi() {
- if (mCurrentUi != UI_EDITING_PRINT_JOB) {
- return;
- }
-
- // Content container
- mContentContainer = findViewById(R.id.content_container);
-
- // Copies
- mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
- mCopiesEditText.setOnFocusChangeListener(mFocusListener);
- mCopiesEditText.setText(MIN_COPIES_STRING);
- mCopiesEditText.setSelection(mCopiesEditText.getText().length());
- mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
- if (!TextUtils.equals(mCopiesEditText.getText(), MIN_COPIES_STRING)) {
- mIgnoreNextCopiesChange = true;
- }
- mSpoolerProvider.getSpooler().setPrintJobCopiesNoPersistence(
- mPrintJobId, MIN_COPIES);
-
- // Destination.
- mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
- mDestinationSpinner.setDropDownWidth(ViewGroup.LayoutParams.MATCH_PARENT);
- mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
- mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- if (mDestinationSpinnerAdapter.getCount() > 0) {
- mIgnoreNextDestinationChange = true;
- }
-
- // Media size.
- mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
- mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
- mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- if (mMediaSizeSpinnerAdapter.getCount() > 0) {
- mOldMediaSizeSelectionIndex = 0;
- }
-
- // Color mode.
- mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
- mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
- mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- if (mColorModeSpinnerAdapter.getCount() > 0) {
- mOldColorModeSelectionIndex = 0;
- }
-
- // Orientation
- mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
- mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
- mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- if (mOrientationSpinnerAdapter.getCount() > 0) {
- mIgnoreNextOrientationChange = true;
- }
-
- // Range options
- mRangeOptionsTitle = (TextView) findViewById(R.id.range_options_title);
- mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
- mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
- mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
- if (mRangeOptionsSpinnerAdapter.getCount() > 0) {
- mIgnoreNextRangeOptionChange = true;
- }
-
- // Page range
- mPageRangeTitle = (TextView) findViewById(R.id.page_range_title);
- mPageRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
- mPageRangeEditText.setOnFocusChangeListener(mFocusListener);
- mPageRangeEditText.addTextChangedListener(mRangeTextWatcher);
-
- // Advanced options button.
- mAdvancedPrintOptionsContainer = findViewById(R.id.advanced_settings_container);
- mAdvancedOptionsButton = (Button) findViewById(R.id.advanced_settings_button);
- registerAdvancedPrintOptionsButtonClickListener();
-
- // Print button
- mPrintButton = (Button) findViewById(R.id.print_button);
- registerPrintButtonClickListener();
- }
-
- public boolean updateUi() {
- if (mCurrentUi != UI_EDITING_PRINT_JOB) {
- return false;
- }
- if (isPrintConfirmed() || isCancelled()) {
- mDestinationSpinner.setEnabled(false);
- mCopiesEditText.setEnabled(false);
- mMediaSizeSpinner.setEnabled(false);
- mColorModeSpinner.setEnabled(false);
- mOrientationSpinner.setEnabled(false);
- mRangeOptionsSpinner.setEnabled(false);
- mPageRangeEditText.setEnabled(false);
- mPrintButton.setEnabled(false);
- mAdvancedOptionsButton.setEnabled(false);
- return false;
- }
-
- // If a printer with capabilities is selected, then we enabled all options.
- boolean allOptionsEnabled = false;
- final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
- if (selectedIndex >= 0) {
- Object item = mDestinationSpinnerAdapter.getItem(selectedIndex);
- if (item instanceof PrinterInfo) {
- PrinterInfo printer = (PrinterInfo) item;
- if (printer.getCapabilities() != null
- && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) {
- allOptionsEnabled = true;
- }
- }
- }
-
- if (!allOptionsEnabled) {
- mCopiesEditText.setEnabled(false);
- mMediaSizeSpinner.setEnabled(false);
- mColorModeSpinner.setEnabled(false);
- mOrientationSpinner.setEnabled(false);
- mRangeOptionsSpinner.setEnabled(false);
- mPageRangeEditText.setEnabled(false);
- mPrintButton.setEnabled(false);
- mAdvancedOptionsButton.setEnabled(false);
- return false;
- } else {
- boolean someAttributeSelectionChanged = false;
-
- PrinterInfo printer = (PrinterInfo) mDestinationSpinner.getSelectedItem();
- PrinterCapabilitiesInfo capabilities = printer.getCapabilities();
- PrintAttributes defaultAttributes = printer.getCapabilities().getDefaults();
-
- // Media size.
- // Sort the media sizes based on the current locale.
- List<MediaSize> mediaSizes = new ArrayList<MediaSize>(capabilities.getMediaSizes());
- Collections.sort(mediaSizes, mMediaSizeComparator);
-
- // If the media sizes changed, we update the adapter and the spinner.
- boolean mediaSizesChanged = false;
- final int mediaSizeCount = mediaSizes.size();
- if (mediaSizeCount != mMediaSizeSpinnerAdapter.getCount()) {
- mediaSizesChanged = true;
- } else {
- for (int i = 0; i < mediaSizeCount; i++) {
- if (!mediaSizes.get(i).equals(mMediaSizeSpinnerAdapter.getItem(i).value)) {
- mediaSizesChanged = true;
- break;
- }
- }
- }
- if (mediaSizesChanged) {
- // Remember the old media size to try selecting it again.
- int oldMediaSizeNewIndex = AdapterView.INVALID_POSITION;
- MediaSize oldMediaSize = mCurrPrintAttributes.getMediaSize();
-
- // Rebuild the adapter data.
- mMediaSizeSpinnerAdapter.clear();
- for (int i = 0; i < mediaSizeCount; i++) {
- MediaSize mediaSize = mediaSizes.get(i);
- if (mediaSize.asPortrait().equals(oldMediaSize.asPortrait())) {
- // Update the index of the old selection.
- oldMediaSizeNewIndex = i;
- }
- mMediaSizeSpinnerAdapter.add(new SpinnerItem<MediaSize>(
- mediaSize, mediaSize.getLabel(getPackageManager())));
- }
-
- mMediaSizeSpinner.setEnabled(true);
-
- if (oldMediaSizeNewIndex != AdapterView.INVALID_POSITION) {
- // Select the old media size - nothing really changed.
- setMediaSizeSpinnerSelectionNoCallback(oldMediaSizeNewIndex);
- } else {
- // Select the first or the default and mark if selection changed.
- final int mediaSizeIndex = Math.max(mediaSizes.indexOf(
- defaultAttributes.getMediaSize()), 0);
- setMediaSizeSpinnerSelectionNoCallback(mediaSizeIndex);
- if (oldMediaSize.isPortrait()) {
- mCurrPrintAttributes.setMediaSize(mMediaSizeSpinnerAdapter
- .getItem(mediaSizeIndex).value.asPortrait());
- } else {
- mCurrPrintAttributes.setMediaSize(mMediaSizeSpinnerAdapter
- .getItem(mediaSizeIndex).value.asLandscape());
- }
- someAttributeSelectionChanged = true;
- }
- }
- mMediaSizeSpinner.setEnabled(true);
-
- // Color mode.
- final int colorModes = capabilities.getColorModes();
-
- // If the color modes changed, we update the adapter and the spinner.
- boolean colorModesChanged = false;
- if (Integer.bitCount(colorModes) != mColorModeSpinnerAdapter.getCount()) {
- colorModesChanged = true;
- } else {
- int remainingColorModes = colorModes;
- int adapterIndex = 0;
- while (remainingColorModes != 0) {
- final int colorBitOffset = Integer.numberOfTrailingZeros(
- remainingColorModes);
- final int colorMode = 1 << colorBitOffset;
- remainingColorModes &= ~colorMode;
- if (colorMode != mColorModeSpinnerAdapter.getItem(adapterIndex).value) {
- colorModesChanged = true;
- break;
- }
- adapterIndex++;
- }
- }
- if (colorModesChanged) {
- // Remember the old color mode to try selecting it again.
- int oldColorModeNewIndex = AdapterView.INVALID_POSITION;
- final int oldColorMode = mCurrPrintAttributes.getColorMode();
-
- // Rebuild the adapter data.
- mColorModeSpinnerAdapter.clear();
- String[] colorModeLabels = getResources().getStringArray(
- R.array.color_mode_labels);
- int remainingColorModes = colorModes;
- while (remainingColorModes != 0) {
- final int colorBitOffset = Integer.numberOfTrailingZeros(
- remainingColorModes);
- final int colorMode = 1 << colorBitOffset;
- if (colorMode == oldColorMode) {
- // Update the index of the old selection.
- oldColorModeNewIndex = colorBitOffset;
- }
- remainingColorModes &= ~colorMode;
- mColorModeSpinnerAdapter.add(new SpinnerItem<Integer>(colorMode,
- colorModeLabels[colorBitOffset]));
- }
- mColorModeSpinner.setEnabled(true);
- if (oldColorModeNewIndex != AdapterView.INVALID_POSITION) {
- // Select the old color mode - nothing really changed.
- setColorModeSpinnerSelectionNoCallback(oldColorModeNewIndex);
- } else {
- final int selectedColorMode = colorModes & defaultAttributes.getColorMode();
- final int itemCount = mColorModeSpinnerAdapter.getCount();
- for (int i = 0; i < itemCount; i++) {
- SpinnerItem<Integer> item = mColorModeSpinnerAdapter.getItem(i);
- if (selectedColorMode == item.value) {
- setColorModeSpinnerSelectionNoCallback(i);
- mCurrPrintAttributes.setColorMode(selectedColorMode);
- someAttributeSelectionChanged = true;
- }
- }
- }
- }
- mColorModeSpinner.setEnabled(true);
-
- // Orientation
- MediaSize mediaSize = mCurrPrintAttributes.getMediaSize();
- if (mediaSize.isPortrait()
- && mOrientationSpinner.getSelectedItemPosition() != 0) {
- mIgnoreNextOrientationChange = true;
- mOrientationSpinner.setSelection(0);
- } else if (!mediaSize.isPortrait()
- && mOrientationSpinner.getSelectedItemPosition() != 1) {
- mIgnoreNextOrientationChange = true;
- mOrientationSpinner.setSelection(1);
- }
- mOrientationSpinner.setEnabled(true);
-
- // Range options
- PrintDocumentInfo info = mDocument.info;
- if (info != null && info.getPageCount() > 0) {
- if (info.getPageCount() == 1) {
- mRangeOptionsSpinner.setEnabled(false);
- } else {
- mRangeOptionsSpinner.setEnabled(true);
- if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
- if (!mPageRangeEditText.isEnabled()) {
- mPageRangeEditText.setEnabled(true);
- mPageRangeEditText.setVisibility(View.VISIBLE);
- mPageRangeTitle.setVisibility(View.VISIBLE);
- mPageRangeEditText.requestFocus();
- InputMethodManager imm = (InputMethodManager)
- getSystemService(INPUT_METHOD_SERVICE);
- imm.showSoftInput(mPageRangeEditText, 0);
- }
- } else {
- mPageRangeEditText.setEnabled(false);
- mPageRangeEditText.setVisibility(View.INVISIBLE);
- mPageRangeTitle.setVisibility(View.INVISIBLE);
- }
- }
- final int pageCount = mDocument.info.getPageCount();
- String title = (pageCount != PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
- ? getString(R.string.label_pages, String.valueOf(pageCount))
- : getString(R.string.page_count_unknown);
- mRangeOptionsTitle.setText(title);
- } else {
- if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
- mIgnoreNextRangeOptionChange = true;
- mRangeOptionsSpinner.setSelection(0);
- }
- mRangeOptionsSpinner.setEnabled(false);
- mRangeOptionsTitle.setText(getString(R.string.page_count_unknown));
- mPageRangeEditText.setEnabled(false);
- mPageRangeEditText.setVisibility(View.INVISIBLE);
- mPageRangeTitle.setVisibility(View.INVISIBLE);
- }
-
- // Advanced print options
- ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
- if (!TextUtils.isEmpty(getAdvancedOptionsActivityName(serviceName))) {
- mAdvancedPrintOptionsContainer.setVisibility(View.VISIBLE);
- mAdvancedOptionsButton.setEnabled(true);
- } else {
- mAdvancedPrintOptionsContainer.setVisibility(View.GONE);
- mAdvancedOptionsButton.setEnabled(false);
- }
-
- // Print
- if (mDestinationSpinner.getSelectedItemId()
- != DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF) {
- String newText = getString(R.string.print_button);
- if (!TextUtils.equals(newText, mPrintButton.getText())) {
- mPrintButton.setText(R.string.print_button);
- }
- } else {
- String newText = getString(R.string.save_button);
- if (!TextUtils.equals(newText, mPrintButton.getText())) {
- mPrintButton.setText(R.string.save_button);
- }
- }
- if ((mRangeOptionsSpinner.getSelectedItemPosition() == 1
- && (TextUtils.isEmpty(mPageRangeEditText.getText()) || hasErrors()))
- || (mRangeOptionsSpinner.getSelectedItemPosition() == 0
- && (!mController.hasPerformedLayout() || hasErrors()))) {
- mPrintButton.setEnabled(false);
- } else {
- mPrintButton.setEnabled(true);
- }
-
- // Copies
- if (mDestinationSpinner.getSelectedItemId()
- != DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF) {
- mCopiesEditText.setEnabled(true);
- } else {
- mCopiesEditText.setEnabled(false);
- }
- if (mCopiesEditText.getError() == null
- && TextUtils.isEmpty(mCopiesEditText.getText())) {
- mIgnoreNextCopiesChange = true;
- mCopiesEditText.setText(String.valueOf(MIN_COPIES));
- mCopiesEditText.requestFocus();
- }
-
- return someAttributeSelectionChanged;
- }
- }
-
- private String getAdvancedOptionsActivityName(ComponentName serviceName) {
- PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
- List<PrintServiceInfo> printServices = printManager.getEnabledPrintServices();
- final int printServiceCount = printServices.size();
- for (int i = 0; i < printServiceCount; i ++) {
- PrintServiceInfo printServiceInfo = printServices.get(i);
- ServiceInfo serviceInfo = printServiceInfo.getResolveInfo().serviceInfo;
- if (serviceInfo.name.equals(serviceName.getClassName())
- && serviceInfo.packageName.equals(serviceName.getPackageName())) {
- return printServiceInfo.getAdvancedOptionsActivityName();
- }
- }
- return null;
- }
-
- private void setMediaSizeSpinnerSelectionNoCallback(int position) {
- if (mMediaSizeSpinner.getSelectedItemPosition() != position) {
- mOldMediaSizeSelectionIndex = position;
- mMediaSizeSpinner.setSelection(position);
- }
- }
-
- private void setColorModeSpinnerSelectionNoCallback(int position) {
- if (mColorModeSpinner.getSelectedItemPosition() != position) {
- mOldColorModeSelectionIndex = position;
- mColorModeSpinner.setSelection(position);
- }
- }
-
- private void startSelectPrinterActivity() {
- Intent intent = new Intent(PrintJobConfigActivity.this,
- SelectPrinterActivity.class);
- startActivityForResult(intent, ACTIVITY_REQUEST_SELECT_PRINTER);
- }
-
- private boolean hasErrors() {
- if (mCopiesEditText.getError() != null) {
- return true;
- }
- return mPageRangeEditText.getVisibility() == View.VISIBLE
- && mPageRangeEditText.getError() != null;
- }
-
- private final class SpinnerItem<T> {
- final T value;
- CharSequence label;
-
- public SpinnerItem(T value, CharSequence label) {
- this.value = value;
- this.label = label;
- }
-
- public String toString() {
- return label.toString();
- }
- }
-
- private final class WaitForPrinterCapabilitiesTimeout implements Runnable {
- private static final long GET_CAPABILITIES_TIMEOUT_MILLIS = 10000; // 10sec
-
- private boolean mIsPosted;
-
- public void post() {
- if (!mIsPosted) {
- mDestinationSpinner.postDelayed(this,
- GET_CAPABILITIES_TIMEOUT_MILLIS);
- mIsPosted = true;
- }
- }
-
- public void remove() {
- if (mIsPosted) {
- mIsPosted = false;
- mDestinationSpinner.removeCallbacks(this);
- }
- }
-
- public boolean isPosted() {
- return mIsPosted;
- }
-
- @Override
- public void run() {
- mIsPosted = false;
- if (mDestinationSpinner.getSelectedItemPosition() >= 0) {
- View itemView = mDestinationSpinner.getSelectedView();
- TextView titleView = (TextView) itemView.findViewById(R.id.subtitle);
- try {
- PackageInfo packageInfo = getPackageManager().getPackageInfo(
- mCurrentPrinter.getId().getServiceName().getPackageName(), 0);
- CharSequence service = packageInfo.applicationInfo.loadLabel(
- getPackageManager());
- String subtitle = getString(R.string.printer_unavailable, service.toString());
- titleView.setText(subtitle);
- } catch (NameNotFoundException nnfe) {
- /* ignore */
- }
- }
- }
- }
-
- private final class DestinationAdapter extends BaseAdapter
- implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>{
- private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
-
- private PrinterInfo mFakePdfPrinter;
-
- public DestinationAdapter() {
- getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
- }
-
- public int getPrinterIndex(PrinterId printerId) {
- for (int i = 0; i < getCount(); i++) {
- PrinterInfo printer = (PrinterInfo) getItem(i);
- if (printer != null && printer.getId().equals(printerId)) {
- return i;
- }
- }
- return AdapterView.INVALID_POSITION;
- }
-
- public void ensurePrinterInVisibleAdapterPosition(PrinterId printerId) {
- final int printerCount = mPrinters.size();
- for (int i = 0; i < printerCount; i++) {
- PrinterInfo printer = (PrinterInfo) mPrinters.get(i);
- if (printer.getId().equals(printerId)) {
- // If already in the list - do nothing.
- if (i < getCount() - 2) {
- return;
- }
- // Else replace the last one (two items are not printers).
- final int lastPrinterIndex = getCount() - 3;
- mPrinters.set(i, mPrinters.get(lastPrinterIndex));
- mPrinters.set(lastPrinterIndex, printer);
- notifyDataSetChanged();
- return;
- }
- }
- }
-
- @Override
- public int getCount() {
- if (mFakePdfPrinter == null) {
- return 0;
- }
- return Math.min(mPrinters.size() + 2, DEST_ADAPTER_MAX_ITEM_COUNT);
- }
-
- @Override
- public boolean isEnabled(int position) {
- Object item = getItem(position);
- if (item instanceof PrinterInfo) {
- PrinterInfo printer = (PrinterInfo) item;
- return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
- }
- return true;
- }
-
- @Override
- public Object getItem(int position) {
- if (mPrinters.isEmpty()) {
- if (position == 0 && mFakePdfPrinter != null) {
- return mFakePdfPrinter;
- }
- } else {
- if (position < 1) {
- return mPrinters.get(position);
- }
- if (position == 1 && mFakePdfPrinter != null) {
- return mFakePdfPrinter;
- }
- if (position < getCount() - 1) {
- return mPrinters.get(position - 1);
- }
- }
- return null;
- }
-
- @Override
- public long getItemId(int position) {
- if (mPrinters.isEmpty()) {
- if (mFakePdfPrinter != null) {
- if (position == 0) {
- return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
- } else if (position == 1) {
- return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
- }
- }
- } else {
- if (position == 1 && mFakePdfPrinter != null) {
- return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
- }
- if (position == getCount() - 1) {
- return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
- }
- }
- return position;
- }
-
- @Override
- public View getDropDownView(int position, View convertView,
- ViewGroup parent) {
- View view = getView(position, convertView, parent);
- view.setEnabled(isEnabled(position));
- return view;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(
- R.layout.printer_dropdown_item, parent, false);
- }
-
- CharSequence title = null;
- CharSequence subtitle = null;
- Drawable icon = null;
-
- if (mPrinters.isEmpty()) {
- if (position == 0 && mFakePdfPrinter != null) {
- PrinterInfo printer = (PrinterInfo) getItem(position);
- title = printer.getName();
- } else if (position == 1) {
- title = getString(R.string.all_printers);
- }
- } else {
- if (position == 1 && mFakePdfPrinter != null) {
- PrinterInfo printer = (PrinterInfo) getItem(position);
- title = printer.getName();
- } else if (position == getCount() - 1) {
- title = getString(R.string.all_printers);
- } else {
- PrinterInfo printer = (PrinterInfo) getItem(position);
- title = printer.getName();
- try {
- PackageInfo packageInfo = getPackageManager().getPackageInfo(
- printer.getId().getServiceName().getPackageName(), 0);
- subtitle = packageInfo.applicationInfo.loadLabel(getPackageManager());
- icon = packageInfo.applicationInfo.loadIcon(getPackageManager());
- } catch (NameNotFoundException nnfe) {
- /* ignore */
- }
- }
- }
-
- TextView titleView = (TextView) convertView.findViewById(R.id.title);
- titleView.setText(title);
-
- TextView subtitleView = (TextView) convertView.findViewById(R.id.subtitle);
- if (!TextUtils.isEmpty(subtitle)) {
- subtitleView.setText(subtitle);
- subtitleView.setVisibility(View.VISIBLE);
- } else {
- subtitleView.setText(null);
- subtitleView.setVisibility(View.GONE);
- }
-
- ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
- if (icon != null) {
- iconView.setImageDrawable(icon);
- iconView.setVisibility(View.VISIBLE);
- } else {
- iconView.setVisibility(View.INVISIBLE);
- }
-
- return convertView;
- }
-
- @Override
- public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
- if (id == LOADER_ID_PRINTERS_LOADER) {
- return new FusedPrintersProvider(PrintJobConfigActivity.this);
- }
- return null;
- }
-
- @Override
- public void onLoadFinished(Loader<List<PrinterInfo>> loader,
- List<PrinterInfo> printers) {
- // If this is the first load, create the fake PDF printer.
- // We do this to avoid flicker where the PDF printer is the
- // only one and as soon as the loader loads the favorites
- // it gets switched. Not a great user experience.
- if (mFakePdfPrinter == null) {
- mCurrentPrinter = mFakePdfPrinter = createFakePdfPrinter();
- updatePrintAttributes(mCurrentPrinter.getCapabilities());
- updateUi();
- }
-
- // We rearrange the printers if the user selects a printer
- // not shown in the initial short list. Therefore, we have
- // to keep the printer order.
-
- // No old printers - do not bother keeping their position.
- if (mPrinters.isEmpty()) {
- mPrinters.addAll(printers);
- mEditor.ensureCurrentPrinterSelected();
- notifyDataSetChanged();
- return;
- }
-
- // Add the new printers to a map.
- ArrayMap<PrinterId, PrinterInfo> newPrintersMap =
- new ArrayMap<PrinterId, PrinterInfo>();
- final int printerCount = printers.size();
- for (int i = 0; i < printerCount; i++) {
- PrinterInfo printer = printers.get(i);
- newPrintersMap.put(printer.getId(), printer);
- }
-
- List<PrinterInfo> newPrinters = new ArrayList<PrinterInfo>();
-
- // Update printers we already have.
- final int oldPrinterCount = mPrinters.size();
- for (int i = 0; i < oldPrinterCount; i++) {
- PrinterId oldPrinterId = mPrinters.get(i).getId();
- PrinterInfo updatedPrinter = newPrintersMap.remove(oldPrinterId);
- if (updatedPrinter != null) {
- newPrinters.add(updatedPrinter);
- }
- }
-
- // Add the rest of the new printers, i.e. what is left.
- newPrinters.addAll(newPrintersMap.values());
-
- mPrinters.clear();
- mPrinters.addAll(newPrinters);
-
- mEditor.ensureCurrentPrinterSelected();
- notifyDataSetChanged();
- }
-
- @Override
- public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
- mPrinters.clear();
- notifyDataSetInvalidated();
- }
-
-
- private PrinterInfo createFakePdfPrinter() {
- MediaSize defaultMediaSize = MediaSizeUtils.getDefault(PrintJobConfigActivity.this);
-
- PrinterId printerId = new PrinterId(getComponentName(), "PDF printer");
-
- PrinterCapabilitiesInfo.Builder builder =
- new PrinterCapabilitiesInfo.Builder(printerId);
-
- String[] mediaSizeIds = getResources().getStringArray(
- R.array.pdf_printer_media_sizes);
- final int mediaSizeIdCount = mediaSizeIds.length;
- for (int i = 0; i < mediaSizeIdCount; i++) {
- String id = mediaSizeIds[i];
- MediaSize mediaSize = MediaSize.getStandardMediaSizeById(id);
- builder.addMediaSize(mediaSize, mediaSize.equals(defaultMediaSize));
- }
-
- builder.addResolution(new Resolution("PDF resolution", "PDF resolution",
- 300, 300), true);
- builder.setColorModes(PrintAttributes.COLOR_MODE_COLOR
- | PrintAttributes.COLOR_MODE_MONOCHROME,
- PrintAttributes.COLOR_MODE_COLOR);
-
- return new PrinterInfo.Builder(printerId, getString(R.string.save_as_pdf),
- PrinterInfo.STATUS_IDLE)
- .setCapabilities(builder.build())
- .build();
- }
- }
- }
-
- /**
- * An instance of this class class is intended to be the first focusable
- * in a layout to which the system automatically gives focus. It performs
- * some voodoo to avoid the first tap on it to start an edit mode, rather
- * to bring up the IME, i.e. to get the behavior as if the view was not
- * focused.
- */
- public static final class CustomEditText extends EditText {
- private boolean mClickedBeforeFocus;
- private CharSequence mError;
-
- public CustomEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public boolean performClick() {
- super.performClick();
- if (isFocused() && !mClickedBeforeFocus) {
- clearFocus();
- requestFocus();
- }
- mClickedBeforeFocus = true;
- return true;
- }
-
- @Override
- public CharSequence getError() {
- return mError;
- }
-
- @Override
- public void setError(CharSequence error, Drawable icon) {
- setCompoundDrawables(null, null, icon, null);
- mError = error;
- }
-
- protected void onFocusChanged(boolean gainFocus, int direction,
- Rect previouslyFocusedRect) {
- if (!gainFocus) {
- mClickedBeforeFocus = false;
- }
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- }
- }
-
- private static final class Document {
- public PrintDocumentInfo info;
- public PageRange[] pages;
- }
-
- private static final class PageRangeUtils {
-
- private static final Comparator<PageRange> sComparator = new Comparator<PageRange>() {
- @Override
- public int compare(PageRange lhs, PageRange rhs) {
- return lhs.getStart() - rhs.getStart();
- }
- };
-
- private PageRangeUtils() {
- throw new UnsupportedOperationException();
- }
-
- public static boolean contains(PageRange[] ourRanges, PageRange[] otherRanges) {
- if (ourRanges == null || otherRanges == null) {
- return false;
- }
-
- if (ourRanges.length == 1
- && PageRange.ALL_PAGES.equals(ourRanges[0])) {
- return true;
- }
-
- ourRanges = normalize(ourRanges);
- otherRanges = normalize(otherRanges);
-
- // Note that the code below relies on the ranges being normalized
- // which is they contain monotonically increasing non-intersecting
- // subranges whose start is less that or equal to the end.
- int otherRangeIdx = 0;
- final int ourRangeCount = ourRanges.length;
- final int otherRangeCount = otherRanges.length;
- for (int ourRangeIdx = 0; ourRangeIdx < ourRangeCount; ourRangeIdx++) {
- PageRange ourRange = ourRanges[ourRangeIdx];
- for (; otherRangeIdx < otherRangeCount; otherRangeIdx++) {
- PageRange otherRange = otherRanges[otherRangeIdx];
- if (otherRange.getStart() > ourRange.getEnd()) {
- break;
- }
- if (otherRange.getStart() < ourRange.getStart()
- || otherRange.getEnd() > ourRange.getEnd()) {
- return false;
- }
- }
- }
- if (otherRangeIdx < otherRangeCount) {
- return false;
- }
- return true;
- }
-
- public static PageRange[] normalize(PageRange[] pageRanges) {
- if (pageRanges == null) {
- return null;
- }
- final int oldRangeCount = pageRanges.length;
- if (oldRangeCount <= 1) {
- return pageRanges;
- }
- Arrays.sort(pageRanges, sComparator);
- int newRangeCount = 1;
- for (int i = 0; i < oldRangeCount - 1; i++) {
- newRangeCount++;
- PageRange currentRange = pageRanges[i];
- PageRange nextRange = pageRanges[i + 1];
- if (currentRange.getEnd() + 1 >= nextRange.getStart()) {
- newRangeCount--;
- pageRanges[i] = null;
- pageRanges[i + 1] = new PageRange(currentRange.getStart(),
- Math.max(currentRange.getEnd(), nextRange.getEnd()));
- }
- }
- if (newRangeCount == oldRangeCount) {
- return pageRanges;
- }
- return Arrays.copyOfRange(pageRanges, oldRangeCount - newRangeCount,
- oldRangeCount);
- }
-
- public static void offset(PageRange[] pageRanges, int offset) {
- if (offset == 0) {
- return;
- }
- final int pageRangeCount = pageRanges.length;
- for (int i = 0; i < pageRangeCount; i++) {
- final int start = pageRanges[i].getStart() + offset;
- final int end = pageRanges[i].getEnd() + offset;
- pageRanges[i] = new PageRange(start, end);
- }
- }
- }
-
- private static final class AutoCancellingAnimator
- implements OnAttachStateChangeListener, Runnable {
-
- private ViewPropertyAnimator mAnimator;
-
- private boolean mCancelled;
- private Runnable mEndCallback;
-
- public static AutoCancellingAnimator animate(View view) {
- ViewPropertyAnimator animator = view.animate();
- AutoCancellingAnimator cancellingWrapper =
- new AutoCancellingAnimator(animator);
- view.addOnAttachStateChangeListener(cancellingWrapper);
- return cancellingWrapper;
- }
-
- private AutoCancellingAnimator(ViewPropertyAnimator animator) {
- mAnimator = animator;
- }
-
- public AutoCancellingAnimator alpha(float alpha) {
- mAnimator = mAnimator.alpha(alpha);
- return this;
- }
-
- public void cancel() {
- mAnimator.cancel();
- }
-
- public AutoCancellingAnimator withLayer() {
- mAnimator = mAnimator.withLayer();
- return this;
- }
-
- public AutoCancellingAnimator withEndAction(Runnable callback) {
- mEndCallback = callback;
- mAnimator = mAnimator.withEndAction(this);
- return this;
- }
-
- public AutoCancellingAnimator scaleY(float scale) {
- mAnimator = mAnimator.scaleY(scale);
- return this;
- }
-
- @Override
- public void onViewAttachedToWindow(View v) {
- /* do nothing */
- }
-
- @Override
- public void onViewDetachedFromWindow(View v) {
- cancel();
- }
-
- @Override
- public void run() {
- if (!mCancelled) {
- mEndCallback.run();
- }
- }
- }
-
- private static final class PrintSpoolerProvider implements ServiceConnection {
- private final Context mContext;
- private final Runnable mCallback;
-
- private PrintSpoolerService mSpooler;
-
- public PrintSpoolerProvider(Context context, Runnable callback) {
- mContext = context;
- mCallback = callback;
- Intent intent = new Intent(mContext, PrintSpoolerService.class);
- mContext.bindService(intent, this, 0);
- }
-
- public PrintSpoolerService getSpooler() {
- return mSpooler;
- }
-
- public void destroy() {
- if (mSpooler != null) {
- mContext.unbindService(this);
- }
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mSpooler = ((PrintSpoolerService.PrintSpooler) service).getService();
- if (mSpooler != null) {
- mCallback.run();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- /* do noting - we are in the same process */
- }
- }
-
- private static final class PrintDocumentAdapterObserver
- extends IPrintDocumentAdapterObserver.Stub {
- private final WeakReference<PrintJobConfigActivity> mWeakActvity;
-
- public PrintDocumentAdapterObserver(PrintJobConfigActivity activity) {
- mWeakActvity = new WeakReference<PrintJobConfigActivity>(activity);
- }
-
- @Override
- public void onDestroy() {
- final PrintJobConfigActivity activity = mWeakActvity.get();
- if (activity != null) {
- activity.mController.mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (activity.mController != null) {
- activity.mController.cancel();
- }
- if (activity.mEditor != null) {
- activity.mEditor.cancel();
- }
- activity.finish();
- }
- });
- }
- }
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
deleted file mode 100644
index 615d667..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ /dev/null
@@ -1,1337 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.print.IPrintSpooler;
-import android.print.IPrintSpoolerCallbacks;
-import android.print.IPrintSpoolerClient;
-import android.print.PageRange;
-import android.print.PrintAttributes;
-import android.print.PrintAttributes.Margins;
-import android.print.PrintAttributes.MediaSize;
-import android.print.PrintAttributes.Resolution;
-import android.print.PrintDocumentInfo;
-import android.print.PrintJobId;
-import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Slog;
-import android.util.Xml;
-
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.util.FastXmlSerializer;
-
-import libcore.io.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-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;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Service for exposing some of the {@link PrintSpooler} functionality to
- * another process.
- */
-public final class PrintSpoolerService extends Service {
-
- private static final String LOG_TAG = "PrintSpoolerService";
-
- private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
-
- private static final boolean DEBUG_PERSISTENCE = false;
-
- private static final boolean PERSISTNECE_MANAGER_ENABLED = true;
-
- private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
-
- private static final String PRINT_JOB_FILE_PREFIX = "print_job_";
-
- private static final String PRINT_FILE_EXTENSION = "pdf";
-
- private static final Object sLock = new Object();
-
- private final Object mLock = new Object();
-
- private final List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>();
-
- private static PrintSpoolerService sInstance;
-
- private IPrintSpoolerClient mClient;
-
- private HandlerCaller mHandlerCaller;
-
- private PersistenceManager mPersistanceManager;
-
- private NotificationController mNotificationController;
-
- public static PrintSpoolerService peekInstance() {
- synchronized (sLock) {
- return sInstance;
- }
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mHandlerCaller = new HandlerCaller(this, getMainLooper(),
- new HandlerCallerCallback(), false);
-
- mPersistanceManager = new PersistenceManager();
- mNotificationController = new NotificationController(PrintSpoolerService.this);
-
- synchronized (mLock) {
- mPersistanceManager.readStateLocked();
- handleReadPrintJobsLocked();
- }
-
- synchronized (sLock) {
- sInstance = this;
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return new PrintSpooler();
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- synchronized (mLock) {
- String prefix = (args.length > 0) ? args[0] : "";
- String tab = " ";
-
- pw.append(prefix).append("print jobs:").println();
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- pw.append(prefix).append(tab).append(printJob.toString());
- pw.println();
- }
-
- pw.append(prefix).append("print job files:").println();
- File[] files = getFilesDir().listFiles();
- if (files != null) {
- final int fileCount = files.length;
- for (int i = 0; i < fileCount; i++) {
- File file = files[i];
- if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
- pw.append(prefix).append(tab).append(file.getName()).println();
- }
- }
- }
- }
- }
-
- private void sendOnPrintJobQueued(PrintJobInfo printJob) {
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
- mHandlerCaller.executeOrSendMessage(message);
- }
-
- private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
- mHandlerCaller.executeOrSendMessage(message);
- }
-
- private void sendOnAllPrintJobsHandled() {
- Message message = mHandlerCaller.obtainMessage(
- HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
- mHandlerCaller.executeOrSendMessage(message);
- }
-
- private final class HandlerCallerCallback implements HandlerCaller.Callback {
- public static final int MSG_SET_CLIENT = 1;
- public static final int MSG_ON_PRINT_JOB_QUEUED = 2;
- public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 3;
- public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 4;
- public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 5;
- public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 6;
-
- @Override
- public void executeMessage(Message message) {
- switch (message.what) {
- case MSG_SET_CLIENT: {
- synchronized (mLock) {
- mClient = (IPrintSpoolerClient) message.obj;
- if (mClient != null) {
- Message msg = mHandlerCaller.obtainMessage(
- HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
- mHandlerCaller.sendMessageDelayed(msg,
- CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
- }
- }
- } break;
-
- case MSG_ON_PRINT_JOB_QUEUED: {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- if (mClient != null) {
- try {
- mClient.onPrintJobQueued(printJob);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
- }
- }
- } break;
-
- case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
- ComponentName service = (ComponentName) message.obj;
- if (mClient != null) {
- try {
- mClient.onAllPrintJobsForServiceHandled(service);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for all print jobs per service"
- + " handled.", re);
- }
- }
- } break;
-
- case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
- if (mClient != null) {
- try {
- mClient.onAllPrintJobsHandled();
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
- }
- }
- } break;
-
- case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
- checkAllPrintJobsHandled();
- } break;
-
- case MSG_ON_PRINT_JOB_STATE_CHANGED: {
- if (mClient != null) {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- try {
- mClient.onPrintJobStateChanged(printJob);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for print job state change.", re);
- }
- }
- } break;
- }
- }
- }
-
- public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName,
- int state, int appId) {
- List<PrintJobInfo> foundPrintJobs = null;
- synchronized (mLock) {
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- PrinterId printerId = printJob.getPrinterId();
- final boolean sameComponent = (componentName == null
- || (printerId != null
- && componentName.equals(printerId.getServiceName())));
- final boolean sameAppId = appId == PrintManager.APP_ID_ANY
- || printJob.getAppId() == appId;
- final boolean sameState = (state == printJob.getState())
- || (state == PrintJobInfo.STATE_ANY)
- || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
- && isStateVisibleToUser(printJob.getState()))
- || (state == PrintJobInfo.STATE_ANY_ACTIVE
- && isActiveState(printJob.getState()))
- || (state == PrintJobInfo.STATE_ANY_SCHEDULED
- && isScheduledState(printJob.getState()));
- if (sameComponent && sameAppId && sameState) {
- if (foundPrintJobs == null) {
- foundPrintJobs = new ArrayList<PrintJobInfo>();
- }
- foundPrintJobs.add(printJob);
- }
- }
- }
- return foundPrintJobs;
- }
-
- private boolean isStateVisibleToUser(int state) {
- return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
- || state == PrintJobInfo.STATE_COMPLETED || state == PrintJobInfo.STATE_CANCELED
- || state == PrintJobInfo.STATE_BLOCKED));
- }
-
- public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
- synchronized (mLock) {
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- if (printJob.getId().equals(printJobId)
- && (appId == PrintManager.APP_ID_ANY
- || appId == printJob.getAppId())) {
- return printJob;
- }
- }
- return null;
- }
- }
-
- public void createPrintJob(PrintJobInfo printJob) {
- synchronized (mLock) {
- addPrintJobLocked(printJob);
- setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
-
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
- printJob);
- mHandlerCaller.executeOrSendMessage(message);
- }
- }
-
- private void handleReadPrintJobsLocked() {
- // Make a map with the files for a print job since we may have
- // to delete some. One example of getting orphan files if the
- // spooler crashes while constructing a print job. We do not
- // persist partially populated print jobs under construction to
- // avoid special handling for various attributes missing.
- ArrayMap<PrintJobId, File> fileForJobMap = null;
- File[] files = getFilesDir().listFiles();
- if (files != null) {
- final int fileCount = files.length;
- for (int i = 0; i < fileCount; i++) {
- File file = files[i];
- if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
- if (fileForJobMap == null) {
- fileForJobMap = new ArrayMap<PrintJobId, File>();
- }
- String printJobIdString = file.getName().substring(
- PRINT_JOB_FILE_PREFIX.length(),
- file.getName().indexOf('.'));
- PrintJobId printJobId = PrintJobId.unflattenFromString(
- printJobIdString);
- fileForJobMap.put(printJobId, file);
- }
- }
- }
-
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
-
- // We want to have only the orphan files at the end.
- if (fileForJobMap != null) {
- fileForJobMap.remove(printJob.getId());
- }
-
- switch (printJob.getState()) {
- case PrintJobInfo.STATE_QUEUED:
- case PrintJobInfo.STATE_STARTED:
- case PrintJobInfo.STATE_BLOCKED: {
- // We have a print job that was queued or started or blocked in
- // the past but the device battery died or a crash occurred. In
- // this case we assume the print job failed and let the user
- // decide whether to restart the job or just cancel it.
- setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
- getString(R.string.no_connection_to_printer));
- } break;
- }
- }
-
- if (!mPrintJobs.isEmpty()) {
- // Update the notification.
- mNotificationController.onUpdateNotifications(mPrintJobs);
- }
-
- // Delete the orphan files.
- if (fileForJobMap != null) {
- final int orphanFileCount = fileForJobMap.size();
- for (int i = 0; i < orphanFileCount; i++) {
- File file = fileForJobMap.valueAt(i);
- file.delete();
- }
- }
- }
-
- public void checkAllPrintJobsHandled() {
- synchronized (mLock) {
- if (!hasActivePrintJobsLocked()) {
- notifyOnAllPrintJobsHandled();
- }
- }
- }
-
- public void writePrintJobData(final ParcelFileDescriptor fd, final PrintJobId printJobId) {
- final PrintJobInfo printJob;
- synchronized (mLock) {
- printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- }
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- FileInputStream in = null;
- FileOutputStream out = null;
- try {
- if (printJob != null) {
- File file = generateFileForPrintJob(printJobId);
- in = new FileInputStream(file);
- out = new FileOutputStream(fd.getFileDescriptor());
- }
- final byte[] buffer = new byte[8192];
- while (true) {
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- return null;
- }
- out.write(buffer, 0, readByteCount);
- }
- } catch (FileNotFoundException fnfe) {
- Log.e(LOG_TAG, "Error writing print job data!", fnfe);
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error writing print job data!", ioe);
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(fd);
- }
- Log.i(LOG_TAG, "[END WRITE]");
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
-
- public File generateFileForPrintJob(PrintJobId printJobId) {
- return new File(getFilesDir(), PRINT_JOB_FILE_PREFIX
- + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
- }
-
- private void addPrintJobLocked(PrintJobInfo printJob) {
- mPrintJobs.add(printJob);
- if (DEBUG_PRINT_JOB_LIFECYCLE) {
- Slog.i(LOG_TAG, "[ADD] " + printJob);
- }
- }
-
- private void removeObsoletePrintJobs() {
- synchronized (mLock) {
- boolean persistState = false;
- final int printJobCount = mPrintJobs.size();
- for (int i = printJobCount - 1; i >= 0; i--) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- if (isObsoleteState(printJob.getState())) {
- mPrintJobs.remove(i);
- if (DEBUG_PRINT_JOB_LIFECYCLE) {
- Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString());
- }
- removePrintJobFileLocked(printJob.getId());
- persistState = true;
- }
- }
- if (persistState) {
- mPersistanceManager.writeStateLocked();
- }
- }
- }
-
- private void removePrintJobFileLocked(PrintJobId printJobId) {
- File file = generateFileForPrintJob(printJobId);
- if (file.exists()) {
- file.delete();
- if (DEBUG_PRINT_JOB_LIFECYCLE) {
- Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
- }
- }
- }
-
- public boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
- boolean success = false;
-
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- final int oldState = printJob.getState();
- if (oldState == state) {
- return false;
- }
-
- success = true;
-
- printJob.setState(state);
- printJob.setStateReason(error);
- printJob.setCancelling(false);
-
- if (DEBUG_PRINT_JOB_LIFECYCLE) {
- Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
- }
-
- switch (state) {
- case PrintJobInfo.STATE_COMPLETED:
- case PrintJobInfo.STATE_CANCELED:
- mPrintJobs.remove(printJob);
- removePrintJobFileLocked(printJob.getId());
- // $fall-through$
-
- case PrintJobInfo.STATE_FAILED: {
- PrinterId printerId = printJob.getPrinterId();
- if (printerId != null) {
- ComponentName service = printerId.getServiceName();
- if (!hasActivePrintJobsForServiceLocked(service)) {
- sendOnAllPrintJobsForServiceHandled(service);
- }
- }
- } break;
-
- case PrintJobInfo.STATE_QUEUED: {
- sendOnPrintJobQueued(new PrintJobInfo(printJob));
- } break;
- }
-
- if (shouldPersistPrintJob(printJob)) {
- mPersistanceManager.writeStateLocked();
- }
-
- if (!hasActivePrintJobsLocked()) {
- notifyOnAllPrintJobsHandled();
- }
-
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
- printJob);
- mHandlerCaller.executeOrSendMessage(message);
-
- mNotificationController.onUpdateNotifications(mPrintJobs);
- }
- }
-
- return success;
- }
-
- public boolean hasActivePrintJobsLocked() {
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- if (isActiveState(printJob.getState())) {
- return true;
- }
- }
- return false;
- }
-
- public boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
- final int printJobCount = mPrintJobs.size();
- for (int i = 0; i < printJobCount; i++) {
- PrintJobInfo printJob = mPrintJobs.get(i);
- if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null
- && printJob.getPrinterId().getServiceName().equals(service)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean isObsoleteState(int printJobState) {
- return (isTeminalState(printJobState)
- || printJobState == PrintJobInfo.STATE_QUEUED);
- }
-
- private boolean isScheduledState(int printJobState) {
- return printJobState == PrintJobInfo.STATE_QUEUED
- || printJobState == PrintJobInfo.STATE_STARTED
- || printJobState == PrintJobInfo.STATE_BLOCKED;
- }
-
- private boolean isActiveState(int printJobState) {
- return printJobState == PrintJobInfo.STATE_CREATED
- || printJobState == PrintJobInfo.STATE_QUEUED
- || printJobState == PrintJobInfo.STATE_STARTED
- || printJobState == PrintJobInfo.STATE_BLOCKED;
- }
-
- private boolean isTeminalState(int printJobState) {
- return printJobState == PrintJobInfo.STATE_COMPLETED
- || printJobState == PrintJobInfo.STATE_CANCELED;
- }
-
- public boolean setPrintJobTag(PrintJobId printJobId, String tag) {
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- String printJobTag = printJob.getTag();
- if (printJobTag == null) {
- if (tag == null) {
- return false;
- }
- } else if (printJobTag.equals(tag)) {
- return false;
- }
- printJob.setTag(tag);
- if (shouldPersistPrintJob(printJob)) {
- mPersistanceManager.writeStateLocked();
- }
- return true;
- }
- }
- return false;
- }
-
- public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- printJob.setCancelling(cancelling);
- if (shouldPersistPrintJob(printJob)) {
- mPersistanceManager.writeStateLocked();
- }
- mNotificationController.onUpdateNotifications(mPrintJobs);
-
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
- printJob);
- mHandlerCaller.executeOrSendMessage(message);
- }
- }
- }
-
- public void setPrintJobCopiesNoPersistence(PrintJobId printJobId, int copies) {
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- printJob.setCopies(copies);
- }
- }
- }
-
- public void setPrintJobAdvancedOptionsNoPersistence(PrintJobId printJobId,
- Bundle advancedOptions) {
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- printJob.setAdvancedOptions(advancedOptions);
- }
- }
- }
-
- public void setPrintJobPrintDocumentInfoNoPersistence(PrintJobId printJobId,
- PrintDocumentInfo info) {
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- printJob.setDocumentInfo(info);
- }
- }
- }
-
- public void setPrintJobAttributesNoPersistence(PrintJobId printJobId,
- PrintAttributes attributes) {
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- printJob.setAttributes(attributes);
- }
- }
- }
-
- public void setPrintJobPrinterNoPersistence(PrintJobId printJobId, PrinterInfo printer) {
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- printJob.setPrinterId(printer.getId());
- printJob.setPrinterName(printer.getName());
- }
- }
- }
-
- public void setPrintJobPagesNoPersistence(PrintJobId printJobId, PageRange[] pages) {
- synchronized (mLock) {
- PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
- if (printJob != null) {
- printJob.setPages(pages);
- }
- }
- }
-
- private boolean shouldPersistPrintJob(PrintJobInfo printJob) {
- return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
- }
-
- private void notifyOnAllPrintJobsHandled() {
- // This has to run on the tread that is persisting the current state
- // since this call may result in the system unbinding from the spooler
- // and as a result the spooler process may get killed before the write
- // completes.
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- sendOnAllPrintJobsHandled();
- return null;
- }
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
- }
-
- private final class PersistenceManager {
- private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
-
- private static final String TAG_SPOOLER = "spooler";
- private static final String TAG_JOB = "job";
-
- private static final String TAG_PRINTER_ID = "printerId";
- private static final String TAG_PAGE_RANGE = "pageRange";
- private static final String TAG_ATTRIBUTES = "attributes";
- private static final String TAG_DOCUMENT_INFO = "documentInfo";
-
- private static final String ATTR_ID = "id";
- private static final String ATTR_LABEL = "label";
- private static final String ATTR_LABEL_RES_ID = "labelResId";
- private static final String ATTR_PACKAGE_NAME = "packageName";
- private static final String ATTR_STATE = "state";
- private static final String ATTR_APP_ID = "appId";
- private static final String ATTR_TAG = "tag";
- private static final String ATTR_CREATION_TIME = "creationTime";
- private static final String ATTR_COPIES = "copies";
- private static final String ATTR_PRINTER_NAME = "printerName";
- private static final String ATTR_STATE_REASON = "stateReason";
- private static final String ATTR_CANCELLING = "cancelling";
-
- private static final String TAG_ADVANCED_OPTIONS = "advancedOptions";
- private static final String TAG_ADVANCED_OPTION = "advancedOption";
- private static final String ATTR_KEY = "key";
- private static final String ATTR_TYPE = "type";
- private static final String ATTR_VALUE = "value";
- private static final String TYPE_STRING = "string";
- private static final String TYPE_INT = "int";
-
- private static final String TAG_MEDIA_SIZE = "mediaSize";
- private static final String TAG_RESOLUTION = "resolution";
- private static final String TAG_MARGINS = "margins";
-
- private static final String ATTR_COLOR_MODE = "colorMode";
-
- private static final String ATTR_LOCAL_ID = "localId";
- private static final String ATTR_SERVICE_NAME = "serviceName";
-
- private static final String ATTR_WIDTH_MILS = "widthMils";
- private static final String ATTR_HEIGHT_MILS = "heightMils";
-
- private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
- private static final String ATTR_VERTICAL_DPI = "verticalDpi";
-
- private static final String ATTR_LEFT_MILS = "leftMils";
- private static final String ATTR_TOP_MILS = "topMils";
- private static final String ATTR_RIGHT_MILS = "rightMils";
- private static final String ATTR_BOTTOM_MILS = "bottomMils";
-
- private static final String ATTR_START = "start";
- private static final String ATTR_END = "end";
-
- private static final String ATTR_NAME = "name";
- private static final String ATTR_PAGE_COUNT = "pageCount";
- private static final String ATTR_CONTENT_TYPE = "contentType";
- private static final String ATTR_DATA_SIZE = "dataSize";
-
- private final AtomicFile mStatePersistFile;
-
- private boolean mWriteStateScheduled;
-
- private PersistenceManager() {
- mStatePersistFile = new AtomicFile(new File(getFilesDir(),
- PERSIST_FILE_NAME));
- }
-
- public void writeStateLocked() {
- if (!PERSISTNECE_MANAGER_ENABLED) {
- return;
- }
- if (mWriteStateScheduled) {
- return;
- }
- mWriteStateScheduled = true;
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- synchronized (mLock) {
- mWriteStateScheduled = false;
- doWriteStateLocked();
- }
- return null;
- }
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
- }
-
- private void doWriteStateLocked() {
- if (DEBUG_PERSISTENCE) {
- Log.i(LOG_TAG, "[PERSIST START]");
- }
- FileOutputStream out = null;
- try {
- out = mStatePersistFile.startWrite();
-
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(out, "utf-8");
- serializer.startDocument(null, true);
- serializer.startTag(null, TAG_SPOOLER);
-
- List<PrintJobInfo> printJobs = mPrintJobs;
-
- final int printJobCount = printJobs.size();
- for (int j = 0; j < printJobCount; j++) {
- PrintJobInfo printJob = printJobs.get(j);
-
- if (!shouldPersistPrintJob(printJob)) {
- continue;
- }
-
- serializer.startTag(null, TAG_JOB);
-
- serializer.attribute(null, ATTR_ID, printJob.getId().flattenToString());
- serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
- serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
- serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
- String tag = printJob.getTag();
- if (tag != null) {
- serializer.attribute(null, ATTR_TAG, tag);
- }
- serializer.attribute(null, ATTR_CREATION_TIME, String.valueOf(
- printJob.getCreationTime()));
- serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies()));
- String printerName = printJob.getPrinterName();
- if (!TextUtils.isEmpty(printerName)) {
- serializer.attribute(null, ATTR_PRINTER_NAME, printerName);
- }
- String stateReason = printJob.getStateReason();
- if (!TextUtils.isEmpty(stateReason)) {
- serializer.attribute(null, ATTR_STATE_REASON, stateReason);
- }
- serializer.attribute(null, ATTR_CANCELLING, String.valueOf(
- printJob.isCancelling()));
-
- PrinterId printerId = printJob.getPrinterId();
- if (printerId != null) {
- serializer.startTag(null, TAG_PRINTER_ID);
- serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
- serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
- .flattenToString());
- serializer.endTag(null, TAG_PRINTER_ID);
- }
-
- PageRange[] pages = printJob.getPages();
- if (pages != null) {
- for (int i = 0; i < pages.length; i++) {
- serializer.startTag(null, TAG_PAGE_RANGE);
- serializer.attribute(null, ATTR_START, String.valueOf(
- pages[i].getStart()));
- serializer.attribute(null, ATTR_END, String.valueOf(
- pages[i].getEnd()));
- serializer.endTag(null, TAG_PAGE_RANGE);
- }
- }
-
- PrintAttributes attributes = printJob.getAttributes();
- if (attributes != null) {
- serializer.startTag(null, TAG_ATTRIBUTES);
-
- final int colorMode = attributes.getColorMode();
- serializer.attribute(null, ATTR_COLOR_MODE,
- String.valueOf(colorMode));
-
- MediaSize mediaSize = attributes.getMediaSize();
- if (mediaSize != null) {
- serializer.startTag(null, TAG_MEDIA_SIZE);
- serializer.attribute(null, ATTR_ID, mediaSize.getId());
- serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
- mediaSize.getWidthMils()));
- serializer.attribute(null, ATTR_HEIGHT_MILS, String.valueOf(
- mediaSize.getHeightMils()));
- // We prefer to store only the package name and
- // resource id and fallback to the label.
- if (!TextUtils.isEmpty(mediaSize.mPackageName)
- && mediaSize.mLabelResId > 0) {
- serializer.attribute(null, ATTR_PACKAGE_NAME,
- mediaSize.mPackageName);
- serializer.attribute(null, ATTR_LABEL_RES_ID,
- String.valueOf(mediaSize.mLabelResId));
- } else {
- serializer.attribute(null, ATTR_LABEL,
- mediaSize.getLabel(getPackageManager()));
- }
- serializer.endTag(null, TAG_MEDIA_SIZE);
- }
-
- Resolution resolution = attributes.getResolution();
- if (resolution != null) {
- serializer.startTag(null, TAG_RESOLUTION);
- serializer.attribute(null, ATTR_ID, resolution.getId());
- serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
- resolution.getHorizontalDpi()));
- serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
- resolution.getVerticalDpi()));
- serializer.attribute(null, ATTR_LABEL,
- resolution.getLabel());
- serializer.endTag(null, TAG_RESOLUTION);
- }
-
- Margins margins = attributes.getMinMargins();
- if (margins != null) {
- serializer.startTag(null, TAG_MARGINS);
- serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
- margins.getLeftMils()));
- serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
- margins.getTopMils()));
- serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
- margins.getRightMils()));
- serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
- margins.getBottomMils()));
- serializer.endTag(null, TAG_MARGINS);
- }
-
- serializer.endTag(null, TAG_ATTRIBUTES);
- }
-
- PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
- if (documentInfo != null) {
- serializer.startTag(null, TAG_DOCUMENT_INFO);
- serializer.attribute(null, ATTR_NAME, documentInfo.getName());
- serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
- documentInfo.getContentType()));
- serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
- documentInfo.getPageCount()));
- serializer.attribute(null, ATTR_DATA_SIZE, String.valueOf(
- documentInfo.getDataSize()));
- serializer.endTag(null, TAG_DOCUMENT_INFO);
- }
-
- Bundle advancedOptions = printJob.getAdvancedOptions();
- if (advancedOptions != null) {
- serializer.startTag(null, TAG_ADVANCED_OPTIONS);
- for (String key : advancedOptions.keySet()) {
- Object value = advancedOptions.get(key);
- if (value instanceof String) {
- String stringValue = (String) value;
- serializer.startTag(null, TAG_ADVANCED_OPTION);
- serializer.attribute(null, ATTR_KEY, key);
- serializer.attribute(null, ATTR_TYPE, TYPE_STRING);
- serializer.attribute(null, ATTR_VALUE, stringValue);
- serializer.endTag(null, TAG_ADVANCED_OPTION);
- } else if (value instanceof Integer) {
- String intValue = Integer.toString((Integer) value);
- serializer.startTag(null, TAG_ADVANCED_OPTION);
- serializer.attribute(null, ATTR_KEY, key);
- serializer.attribute(null, ATTR_TYPE, TYPE_INT);
- serializer.attribute(null, ATTR_VALUE, intValue);
- serializer.endTag(null, TAG_ADVANCED_OPTION);
- }
- }
- serializer.endTag(null, TAG_ADVANCED_OPTIONS);
- }
-
- serializer.endTag(null, TAG_JOB);
-
- if (DEBUG_PERSISTENCE) {
- Log.i(LOG_TAG, "[PERSISTED] " + printJob);
- }
- }
-
- serializer.endTag(null, TAG_SPOOLER);
- serializer.endDocument();
- mStatePersistFile.finishWrite(out);
- if (DEBUG_PERSISTENCE) {
- Log.i(LOG_TAG, "[PERSIST END]");
- }
- } catch (IOException e) {
- Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
- mStatePersistFile.failWrite(out);
- } finally {
- IoUtils.closeQuietly(out);
- }
- }
-
- public void readStateLocked() {
- if (!PERSISTNECE_MANAGER_ENABLED) {
- return;
- }
- FileInputStream in = null;
- try {
- in = mStatePersistFile.openRead();
- } catch (FileNotFoundException e) {
- Log.i(LOG_TAG, "No existing print spooler state.");
- return;
- }
- try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, null);
- parseState(parser);
- } catch (IllegalStateException ise) {
- Slog.w(LOG_TAG, "Failed parsing ", ise);
- } catch (NullPointerException npe) {
- Slog.w(LOG_TAG, "Failed parsing ", npe);
- } catch (NumberFormatException nfe) {
- Slog.w(LOG_TAG, "Failed parsing ", nfe);
- } catch (XmlPullParserException xppe) {
- Slog.w(LOG_TAG, "Failed parsing ", xppe);
- } catch (IOException ioe) {
- Slog.w(LOG_TAG, "Failed parsing ", ioe);
- } catch (IndexOutOfBoundsException iobe) {
- Slog.w(LOG_TAG, "Failed parsing ", iobe);
- } finally {
- IoUtils.closeQuietly(in);
- }
- }
-
- private void parseState(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
- parser.next();
-
- while (parsePrintJob(parser)) {
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
- }
-
- private boolean parsePrintJob(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- skipEmptyTextTags(parser);
- if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
- return false;
- }
-
- PrintJobInfo printJob = new PrintJobInfo();
-
- PrintJobId printJobId = PrintJobId.unflattenFromString(
- parser.getAttributeValue(null, ATTR_ID));
- printJob.setId(printJobId);
- String label = parser.getAttributeValue(null, ATTR_LABEL);
- printJob.setLabel(label);
- final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
- printJob.setState(state);
- final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
- printJob.setAppId(appId);
- String tag = parser.getAttributeValue(null, ATTR_TAG);
- printJob.setTag(tag);
- String creationTime = parser.getAttributeValue(null, ATTR_CREATION_TIME);
- printJob.setCreationTime(Long.parseLong(creationTime));
- String copies = parser.getAttributeValue(null, ATTR_COPIES);
- printJob.setCopies(Integer.parseInt(copies));
- String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
- printJob.setPrinterName(printerName);
- String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON);
- printJob.setStateReason(stateReason);
- String cancelling = parser.getAttributeValue(null, ATTR_CANCELLING);
- printJob.setCancelling(!TextUtils.isEmpty(cancelling)
- ? Boolean.parseBoolean(cancelling) : false);
-
- parser.next();
-
- skipEmptyTextTags(parser);
- if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
- String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
- ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
- null, ATTR_SERVICE_NAME));
- printJob.setPrinterId(new PrinterId(service, localId));
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- List<PageRange> pageRanges = null;
- while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
- final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
- final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
- PageRange pageRange = new PageRange(start, end);
- if (pageRanges == null) {
- pageRanges = new ArrayList<PageRange>();
- }
- pageRanges.add(pageRange);
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
- parser.next();
- skipEmptyTextTags(parser);
- }
- if (pageRanges != null) {
- PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
- pageRanges.toArray(pageRangesArray);
- printJob.setPages(pageRangesArray);
- }
-
- skipEmptyTextTags(parser);
- if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
-
- PrintAttributes.Builder builder = new PrintAttributes.Builder();
-
- String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
- builder.setColorMode(Integer.parseInt(colorMode));
-
- parser.next();
-
- skipEmptyTextTags(parser);
- if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
- String id = parser.getAttributeValue(null, ATTR_ID);
- label = parser.getAttributeValue(null, ATTR_LABEL);
- final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_WIDTH_MILS));
- final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_HEIGHT_MILS));
- String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
- String labelResIdString = parser.getAttributeValue(null, ATTR_LABEL_RES_ID);
- final int labelResId = (labelResIdString != null)
- ? Integer.parseInt(labelResIdString) : 0;
- label = parser.getAttributeValue(null, ATTR_LABEL);
- MediaSize mediaSize = new MediaSize(id, label, packageName,
- widthMils, heightMils, labelResId);
- builder.setMediaSize(mediaSize);
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
- String id = parser.getAttributeValue(null, ATTR_ID);
- label = parser.getAttributeValue(null, ATTR_LABEL);
- final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_HORIZONTAL_DPI));
- final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_VERTICAL_DPI));
- Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
- builder.setResolution(resolution);
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
- final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_LEFT_MILS));
- final int topMils = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_TOP_MILS));
- final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_RIGHT_MILS));
- final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_BOTTOM_MILS));
- Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
- builder.setMinMargins(margins);
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
- parser.next();
- }
-
- printJob.setAttributes(builder.build());
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
- String name = parser.getAttributeValue(null, ATTR_NAME);
- final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_PAGE_COUNT));
- final int contentType = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_CONTENT_TYPE));
- final int dataSize = Integer.parseInt(parser.getAttributeValue(null,
- ATTR_DATA_SIZE));
- PrintDocumentInfo info = new PrintDocumentInfo.Builder(name)
- .setPageCount(pageCount)
- .setContentType(contentType).build();
- printJob.setDocumentInfo(info);
- info.setDataSize(dataSize);
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
- parser.next();
- }
-
- skipEmptyTextTags(parser);
- if (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTIONS)) {
- parser.next();
- skipEmptyTextTags(parser);
- Bundle advancedOptions = new Bundle();
- while (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTION)) {
- String key = parser.getAttributeValue(null, ATTR_KEY);
- String value = parser.getAttributeValue(null, ATTR_VALUE);
- String type = parser.getAttributeValue(null, ATTR_TYPE);
- if (TYPE_STRING.equals(type)) {
- advancedOptions.putString(key, value);
- } else if (TYPE_INT.equals(type)) {
- advancedOptions.putInt(key, Integer.valueOf(value));
- }
- parser.next();
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTION);
- parser.next();
- skipEmptyTextTags(parser);
- }
- printJob.setAdvancedOptions(advancedOptions);
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTIONS);
- parser.next();
- }
-
- mPrintJobs.add(printJob);
-
- if (DEBUG_PERSISTENCE) {
- Log.i(LOG_TAG, "[RESTORED] " + printJob);
- }
-
- skipEmptyTextTags(parser);
- expect(parser, XmlPullParser.END_TAG, TAG_JOB);
-
- return true;
- }
-
- private void expect(XmlPullParser parser, int type, String tag)
- throws IOException, XmlPullParserException {
- if (!accept(parser, type, tag)) {
- throw new XmlPullParserException("Exepected event: " + type
- + " and tag: " + tag + " but got event: " + parser.getEventType()
- + " and tag:" + parser.getName());
- }
- }
-
- private void skipEmptyTextTags(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- while (accept(parser, XmlPullParser.TEXT, null)
- && "\n".equals(parser.getText())) {
- parser.next();
- }
- }
-
- private boolean accept(XmlPullParser parser, int type, String tag)
- throws IOException, XmlPullParserException {
- if (parser.getEventType() != type) {
- return false;
- }
- if (tag != null) {
- if (!tag.equals(parser.getName())) {
- return false;
- }
- } else if (parser.getName() != null) {
- return false;
- }
- return true;
- }
- }
-
- final class PrintSpooler extends IPrintSpooler.Stub {
- @Override
- public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
- ComponentName componentName, int state, int appId, int sequence)
- throws RemoteException {
- List<PrintJobInfo> printJobs = null;
- try {
- printJobs = PrintSpoolerService.this.getPrintJobInfos(
- componentName, state, appId);
- } finally {
- callback.onGetPrintJobInfosResult(printJobs, sequence);
- }
- }
-
- @Override
- public void getPrintJobInfo(PrintJobId printJobId, IPrintSpoolerCallbacks callback,
- int appId, int sequence) throws RemoteException {
- PrintJobInfo printJob = null;
- try {
- printJob = PrintSpoolerService.this.getPrintJobInfo(printJobId, appId);
- } finally {
- callback.onGetPrintJobInfoResult(printJob, sequence);
- }
- }
-
- @Override
- public void createPrintJob(PrintJobInfo printJob) {
- PrintSpoolerService.this.createPrintJob(printJob);
- }
-
- @Override
- public void setPrintJobState(PrintJobId printJobId, int state, String error,
- IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
- boolean success = false;
- try {
- success = PrintSpoolerService.this.setPrintJobState(
- printJobId, state, error);
- } finally {
- callback.onSetPrintJobStateResult(success, sequece);
- }
- }
-
- @Override
- public void setPrintJobTag(PrintJobId printJobId, String tag,
- IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
- boolean success = false;
- try {
- success = PrintSpoolerService.this.setPrintJobTag(printJobId, tag);
- } finally {
- callback.onSetPrintJobTagResult(success, sequece);
- }
- }
-
- @Override
- public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
- PrintSpoolerService.this.writePrintJobData(fd, printJobId);
- }
-
- @Override
- public void setClient(IPrintSpoolerClient client) {
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_SET_CLIENT, client);
- mHandlerCaller.executeOrSendMessage(message);
- }
-
- @Override
- public void removeObsoletePrintJobs() {
- PrintSpoolerService.this.removeObsoletePrintJobs();
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- PrintSpoolerService.this.dump(fd, writer, args);
- }
-
- @Override
- public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
- PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
- }
-
- public PrintSpoolerService getService() {
- return PrintSpoolerService.this;
- }
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
deleted file mode 100644
index d9ccb5d..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.print.ILayoutResultCallback;
-import android.print.IPrintDocumentAdapter;
-import android.print.IWriteResultCallback;
-import android.print.PageRange;
-import android.print.PrintAttributes;
-import android.util.Log;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * This class represents a remote print document adapter instance.
- */
-final class RemotePrintDocumentAdapter {
- private static final String LOG_TAG = "RemotePrintDocumentAdapter";
-
- private static final boolean DEBUG = false;
-
- private final IPrintDocumentAdapter mRemoteInterface;
-
- private final File mFile;
-
- public RemotePrintDocumentAdapter(IPrintDocumentAdapter printAdatper, File file) {
- mRemoteInterface = printAdatper;
- mFile = file;
- }
-
- public void start() {
- if (DEBUG) {
- Log.i(LOG_TAG, "start()");
- }
- try {
- mRemoteInterface.start();
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling start()", re);
- }
- }
-
- public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
- ILayoutResultCallback callback, Bundle metadata, int sequence) {
- if (DEBUG) {
- Log.i(LOG_TAG, "layout()");
- }
- try {
- mRemoteInterface.layout(oldAttributes, newAttributes, callback, metadata, sequence);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling layout()", re);
- }
- }
-
- public void write(final PageRange[] pages, final IWriteResultCallback callback,
- final int sequence) {
- if (DEBUG) {
- Log.i(LOG_TAG, "write()");
- }
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- InputStream in = null;
- OutputStream out = null;
- ParcelFileDescriptor source = null;
- ParcelFileDescriptor sink = null;
- try {
- ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- source = pipe[0];
- sink = pipe[1];
-
- in = new FileInputStream(source.getFileDescriptor());
- out = new FileOutputStream(mFile);
-
- // Async call to initiate the other process writing the data.
- mRemoteInterface.write(pages, sink, callback, sequence);
-
- // Close the source. It is now held by the client.
- sink.close();
- sink = null;
-
- // Read the data.
- final byte[] buffer = new byte[8192];
- while (true) {
- final int readByteCount = in.read(buffer);
- if (readByteCount < 0) {
- break;
- }
- out.write(buffer, 0, readByteCount);
- }
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling write()", re);
- } catch (IOException ioe) {
- Log.e(LOG_TAG, "Error calling write()", ioe);
- } finally {
- IoUtils.closeQuietly(in);
- IoUtils.closeQuietly(out);
- IoUtils.closeQuietly(sink);
- IoUtils.closeQuietly(source);
- }
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
- }
-
- public void finish() {
- if (DEBUG) {
- Log.i(LOG_TAG, "finish()");
- }
- try {
- mRemoteInterface.finish();
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling finish()", re);
- }
- }
-
- public void cancel() {
- if (DEBUG) {
- Log.i(LOG_TAG, "cancel()");
- }
- try {
- mRemoteInterface.cancel();
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error calling cancel()", re);
- }
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterActivity.java
deleted file mode 100644
index 141dbd1..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterActivity.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.print.PrinterId;
-
-import com.android.printspooler.SelectPrinterFragment.OnPrinterSelectedListener;
-
-public class SelectPrinterActivity extends Activity implements OnPrinterSelectedListener {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.select_printer_activity);
- }
-
- @Override
- public void onPrinterSelected(PrinterId printer) {
- Intent intent = new Intent();
- intent.putExtra(PrintJobConfigActivity.INTENT_EXTRA_PRINTER_ID, printer);
- setResult(RESULT_OK, intent);
- finish();
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
deleted file mode 100644
index fe5920c..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
- * Copyright (C) 2013 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.printspooler;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.app.LoaderManager;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.Loader;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.database.DataSetObserver;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.print.PrintManager;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
-import android.printservice.PrintServiceInfo;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.ArrayAdapter;
-import android.widget.BaseAdapter;
-import android.widget.Filter;
-import android.widget.Filterable;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.SearchView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This is a fragment for selecting a printer.
- */
-public final class SelectPrinterFragment extends Fragment {
-
- private static final String LOG_TAG = "SelectPrinterFragment";
-
- private static final int LOADER_ID_PRINTERS_LOADER = 1;
-
- private static final String FRAGMRNT_TAG_ADD_PRINTER_DIALOG =
- "FRAGMRNT_TAG_ADD_PRINTER_DIALOG";
-
- private static final String FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS =
- "FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS";
-
- private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
-
- private final ArrayList<PrintServiceInfo> mAddPrinterServices =
- new ArrayList<PrintServiceInfo>();
-
- private ListView mListView;
-
- private AnnounceFilterResult mAnnounceFilterResult;
-
- public static interface OnPrinterSelectedListener {
- public void onPrinterSelected(PrinterId printerId);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
- getActivity().getActionBar().setIcon(R.drawable.ic_menu_print);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View content = inflater.inflate(R.layout.select_printer_fragment, container, false);
-
- // Hook up the list view.
- mListView = (ListView) content.findViewById(android.R.id.list);
- final DestinationAdapter adapter = new DestinationAdapter();
- adapter.registerDataSetObserver(new DataSetObserver() {
- @Override
- public void onChanged() {
- if (!getActivity().isFinishing() && adapter.getCount() <= 0) {
- updateEmptyView(adapter);
- }
- }
-
- @Override
- public void onInvalidated() {
- if (!getActivity().isFinishing()) {
- updateEmptyView(adapter);
- }
- }
- });
- mListView.setAdapter(adapter);
-
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (!((DestinationAdapter) mListView.getAdapter()).isActionable(position)) {
- return;
- }
- PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
- Activity activity = getActivity();
- if (activity instanceof OnPrinterSelectedListener) {
- ((OnPrinterSelectedListener) activity).onPrinterSelected(printer.getId());
- } else {
- throw new IllegalStateException("the host activity must implement"
- + " OnPrinterSelectedListener");
- }
- }
- });
-
- registerForContextMenu(mListView);
-
- return content;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.select_printer_activity, menu);
-
- MenuItem searchItem = menu.findItem(R.id.action_search);
- SearchView searchView = (SearchView) searchItem.getActionView();
- searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- return true;
- }
-
- @Override
- public boolean onQueryTextChange(String searchString) {
- ((DestinationAdapter) mListView.getAdapter()).getFilter().filter(searchString);
- return true;
- }
- });
- searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {
- if (AccessibilityManager.getInstance(getActivity()).isEnabled()) {
- view.announceForAccessibility(getString(
- R.string.print_search_box_shown_utterance));
- }
- }
- @Override
- public void onViewDetachedFromWindow(View view) {
- Activity activity = getActivity();
- if (activity != null && !activity.isFinishing()
- && AccessibilityManager.getInstance(activity).isEnabled()) {
- view.announceForAccessibility(getString(
- R.string.print_search_box_hidden_utterance));
- }
- }
- });
-
- if (mAddPrinterServices.isEmpty()) {
- menu.removeItem(R.id.action_add_printer);
- }
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
- if (view == mListView) {
- final int position = ((AdapterContextMenuInfo) menuInfo).position;
- PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
-
- menu.setHeaderTitle(printer.getName());
-
- // Add the select menu item if applicable.
- if (printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) {
- MenuItem selectItem = menu.add(Menu.NONE, R.string.print_select_printer,
- Menu.NONE, R.string.print_select_printer);
- Intent intent = new Intent();
- intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
- selectItem.setIntent(intent);
- }
-
- // Add the forget menu item if applicable.
- FusedPrintersProvider provider = (FusedPrintersProvider) (Loader<?>)
- getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
- if (provider.isFavoritePrinter(printer.getId())) {
- MenuItem forgetItem = menu.add(Menu.NONE, R.string.print_forget_printer,
- Menu.NONE, R.string.print_forget_printer);
- Intent intent = new Intent();
- intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
- forgetItem.setIntent(intent);
- }
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.string.print_select_printer: {
- PrinterId printerId = (PrinterId) item.getIntent().getParcelableExtra(
- EXTRA_PRINTER_ID);
- Activity activity = getActivity();
- if (activity instanceof OnPrinterSelectedListener) {
- ((OnPrinterSelectedListener) activity).onPrinterSelected(printerId);
- } else {
- throw new IllegalStateException("the host activity must implement"
- + " OnPrinterSelectedListener");
- }
- } return true;
-
- case R.string.print_forget_printer: {
- PrinterId printerId = (PrinterId) item.getIntent().getParcelableExtra(
- EXTRA_PRINTER_ID);
- FusedPrintersProvider provider = (FusedPrintersProvider) (Loader<?>)
- getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
- provider.forgetFavoritePrinter(printerId);
- } return true;
- }
- return false;
- }
-
- @Override
- public void onResume() {
- updateAddPrintersAdapter();
- getActivity().invalidateOptionsMenu();
- super.onResume();
- }
-
- @Override
- public void onPause() {
- if (mAnnounceFilterResult != null) {
- mAnnounceFilterResult.remove();
- }
- super.onPause();
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.action_add_printer) {
- showAddPrinterSelectionDialog();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- private void updateAddPrintersAdapter() {
- mAddPrinterServices.clear();
-
- // Get all enabled print services.
- PrintManager printManager = (PrintManager) getActivity()
- .getSystemService(Context.PRINT_SERVICE);
- List<PrintServiceInfo> enabledServices = printManager.getEnabledPrintServices();
-
- // No enabled print services - done.
- if (enabledServices.isEmpty()) {
- return;
- }
-
- // Find the services with valid add printers activities.
- final int enabledServiceCount = enabledServices.size();
- for (int i = 0; i < enabledServiceCount; i++) {
- PrintServiceInfo enabledService = enabledServices.get(i);
-
- // No add printers activity declared - done.
- if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) {
- continue;
- }
-
- ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
- ComponentName addPrintersComponentName = new ComponentName(
- serviceInfo.packageName, enabledService.getAddPrintersActivityName());
- Intent addPritnersIntent = new Intent()
- .setComponent(addPrintersComponentName);
-
- // The add printers activity is valid - add it.
- PackageManager pm = getActivity().getPackageManager();
- List<ResolveInfo> resolvedActivities = pm.queryIntentActivities(addPritnersIntent, 0);
- if (!resolvedActivities.isEmpty()) {
- // The activity is a component name, therefore it is one or none.
- ActivityInfo activityInfo = resolvedActivities.get(0).activityInfo;
- if (activityInfo.exported
- && (activityInfo.permission == null
- || pm.checkPermission(activityInfo.permission,
- getActivity().getPackageName())
- == PackageManager.PERMISSION_GRANTED)) {
- mAddPrinterServices.add(enabledService);
- }
- }
- }
- }
-
- private void showAddPrinterSelectionDialog() {
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- Fragment oldFragment = getFragmentManager().findFragmentByTag(
- FRAGMRNT_TAG_ADD_PRINTER_DIALOG);
- if (oldFragment != null) {
- transaction.remove(oldFragment);
- }
- AddPrinterAlertDialogFragment newFragment = new AddPrinterAlertDialogFragment();
- Bundle arguments = new Bundle();
- arguments.putParcelableArrayList(FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS,
- mAddPrinterServices);
- newFragment.setArguments(arguments);
- transaction.add(newFragment, FRAGMRNT_TAG_ADD_PRINTER_DIALOG);
- transaction.commit();
- }
-
- public void updateEmptyView(DestinationAdapter adapter) {
- if (mListView.getEmptyView() == null) {
- View emptyView = getActivity().findViewById(R.id.empty_print_state);
- mListView.setEmptyView(emptyView);
- }
- TextView titleView = (TextView) getActivity().findViewById(R.id.title);
- View progressBar = getActivity().findViewById(R.id.progress_bar);
- if (adapter.getUnfilteredCount() <= 0) {
- titleView.setText(R.string.print_searching_for_printers);
- progressBar.setVisibility(View.VISIBLE);
- } else {
- titleView.setText(R.string.print_no_printers);
- progressBar.setVisibility(View.GONE);
- }
- }
-
- private void announceSearchResultIfNeeded() {
- if (AccessibilityManager.getInstance(getActivity()).isEnabled()) {
- if (mAnnounceFilterResult == null) {
- mAnnounceFilterResult = new AnnounceFilterResult();
- }
- mAnnounceFilterResult.post();
- }
- }
-
- public static class AddPrinterAlertDialogFragment extends DialogFragment {
-
- private String mAddPrintServiceItem;
-
- @Override
- @SuppressWarnings("unchecked")
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.choose_print_service);
-
- final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
- getArguments().getParcelableArrayList(FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS);
-
- final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
- getActivity(), android.R.layout.simple_list_item_1);
- final int printServiceCount = printServices.size();
- for (int i = 0; i < printServiceCount; i++) {
- PrintServiceInfo printService = printServices.get(i);
- adapter.add(printService.getResolveInfo().loadLabel(
- getActivity().getPackageManager()).toString());
- }
- final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
- Settings.Secure.PRINT_SERVICE_SEARCH_URI);
- final Intent marketIntent;
- if (!TextUtils.isEmpty(searchUri)) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
- if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) {
- marketIntent = intent;
- mAddPrintServiceItem = getString(R.string.add_print_service_label);
- adapter.add(mAddPrintServiceItem);
- } else {
- marketIntent = null;
- }
- } else {
- marketIntent = null;
- }
-
- builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String item = adapter.getItem(which);
- if (item == mAddPrintServiceItem) {
- try {
- startActivity(marketIntent);
- } catch (ActivityNotFoundException anfe) {
- Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
- }
- } else {
- PrintServiceInfo printService = printServices.get(which);
- ComponentName componentName = new ComponentName(
- printService.getResolveInfo().serviceInfo.packageName,
- printService.getAddPrintersActivityName());
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setComponent(componentName);
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException anfe) {
- Log.w(LOG_TAG, "Couldn't start settings activity", anfe);
- }
- }
- }
- });
-
- return builder.create();
- }
- }
-
- private final class DestinationAdapter extends BaseAdapter
- implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>, Filterable {
-
- private final Object mLock = new Object();
-
- private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
-
- private final List<PrinterInfo> mFilteredPrinters = new ArrayList<PrinterInfo>();
-
- private CharSequence mLastSearchString;
-
- public DestinationAdapter() {
- getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
- }
-
- @Override
- public Filter getFilter() {
- return new Filter() {
- @Override
- protected FilterResults performFiltering(CharSequence constraint) {
- synchronized (mLock) {
- if (TextUtils.isEmpty(constraint)) {
- return null;
- }
- FilterResults results = new FilterResults();
- List<PrinterInfo> filteredPrinters = new ArrayList<PrinterInfo>();
- String constraintLowerCase = constraint.toString().toLowerCase();
- final int printerCount = mPrinters.size();
- for (int i = 0; i < printerCount; i++) {
- PrinterInfo printer = mPrinters.get(i);
- if (printer.getName().toLowerCase().contains(constraintLowerCase)) {
- filteredPrinters.add(printer);
- }
- }
- results.values = filteredPrinters;
- results.count = filteredPrinters.size();
- return results;
- }
- }
-
- @Override
- @SuppressWarnings("unchecked")
- protected void publishResults(CharSequence constraint, FilterResults results) {
- final boolean resultCountChanged;
- synchronized (mLock) {
- final int oldPrinterCount = mFilteredPrinters.size();
- mLastSearchString = constraint;
- mFilteredPrinters.clear();
- if (results == null) {
- mFilteredPrinters.addAll(mPrinters);
- } else {
- List<PrinterInfo> printers = (List<PrinterInfo>) results.values;
- mFilteredPrinters.addAll(printers);
- }
- resultCountChanged = (oldPrinterCount != mFilteredPrinters.size());
- }
- if (resultCountChanged) {
- announceSearchResultIfNeeded();
- }
- notifyDataSetChanged();
- }
- };
- }
-
- public int getUnfilteredCount() {
- synchronized (mLock) {
- return mPrinters.size();
- }
- }
-
- @Override
- public int getCount() {
- synchronized (mLock) {
- return mFilteredPrinters.size();
- }
- }
-
- @Override
- public Object getItem(int position) {
- synchronized (mLock) {
- return mFilteredPrinters.get(position);
- }
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getDropDownView(int position, View convertView,
- ViewGroup parent) {
- return getView(position, convertView, parent);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = getActivity().getLayoutInflater().inflate(
- R.layout.printer_list_item, parent, false);
- }
-
- convertView.setEnabled(isActionable(position));
-
- CharSequence title = null;
- CharSequence subtitle = null;
- Drawable icon = null;
-
- PrinterInfo printer = (PrinterInfo) getItem(position);
- title = printer.getName();
- try {
- PackageManager pm = getActivity().getPackageManager();
- PackageInfo packageInfo = pm.getPackageInfo(printer.getId()
- .getServiceName().getPackageName(), 0);
- subtitle = packageInfo.applicationInfo.loadLabel(pm);
- icon = packageInfo.applicationInfo.loadIcon(pm);
- } catch (NameNotFoundException nnfe) {
- /* ignore */
- }
-
- TextView titleView = (TextView) convertView.findViewById(R.id.title);
- titleView.setText(title);
-
- TextView subtitleView = (TextView) convertView.findViewById(R.id.subtitle);
- if (!TextUtils.isEmpty(subtitle)) {
- subtitleView.setText(subtitle);
- subtitleView.setVisibility(View.VISIBLE);
- } else {
- subtitleView.setText(null);
- subtitleView.setVisibility(View.GONE);
- }
-
-
- ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
- if (icon != null) {
- iconView.setImageDrawable(icon);
- iconView.setVisibility(View.VISIBLE);
- } else {
- iconView.setVisibility(View.GONE);
- }
-
- return convertView;
- }
-
- public boolean isActionable(int position) {
- PrinterInfo printer = (PrinterInfo) getItem(position);
- return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
- }
-
- @Override
- public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
- if (id == LOADER_ID_PRINTERS_LOADER) {
- return new FusedPrintersProvider(getActivity());
- }
- return null;
- }
-
- @Override
- public void onLoadFinished(Loader<List<PrinterInfo>> loader,
- List<PrinterInfo> printers) {
- synchronized (mLock) {
- mPrinters.clear();
- mPrinters.addAll(printers);
- mFilteredPrinters.clear();
- mFilteredPrinters.addAll(printers);
- if (!TextUtils.isEmpty(mLastSearchString)) {
- getFilter().filter(mLastSearchString);
- }
- }
- notifyDataSetChanged();
- }
-
- @Override
- public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
- synchronized (mLock) {
- mPrinters.clear();
- mFilteredPrinters.clear();
- }
- notifyDataSetInvalidated();
- }
- }
-
- private final class AnnounceFilterResult implements Runnable {
- private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec
-
- public void post() {
- remove();
- mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
- }
-
- public void remove() {
- mListView.removeCallbacks(this);
- }
-
- @Override
- public void run() {
- final int count = mListView.getAdapter().getCount();
- final String text;
- if (count <= 0) {
- text = getString(R.string.print_no_printers);
- } else {
- text = getActivity().getResources().getQuantityString(
- R.plurals.print_search_result_count_utterance, count, count);
- }
- mListView.announceForAccessibility(text);
- }
- }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
new file mode 100644
index 0000000..929f0fc
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2013 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.printspooler.model;
+
+import android.app.Notification;
+import android.app.Notification.InboxStyle;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.print.IPrintManager;
+import android.print.PrintJobId;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.printspooler.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is responsible for updating the print notifications
+ * based on print job state transitions.
+ */
+final class NotificationController {
+ public static final boolean DEBUG = false;
+
+ public static final String LOG_TAG = "NotificationController";
+
+ private static final String INTENT_ACTION_CANCEL_PRINTJOB = "INTENT_ACTION_CANCEL_PRINTJOB";
+ private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB";
+
+ private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID";
+ private static final String EXTRA_PRINTJOB_LABEL = "EXTRA_PRINTJOB_LABEL";
+ private static final String EXTRA_PRINTER_NAME = "EXTRA_PRINTER_NAME";
+
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+
+ public NotificationController(Context context) {
+ mContext = context;
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ public void onUpdateNotifications(List<PrintJobInfo> printJobs) {
+ List<PrintJobInfo> notifyPrintJobs = new ArrayList<PrintJobInfo>();
+
+ final int printJobCount = printJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = printJobs.get(i);
+ if (shouldNotifyForState(printJob.getState())) {
+ notifyPrintJobs.add(printJob);
+ }
+ }
+
+ updateNotification(notifyPrintJobs);
+ }
+
+ private void updateNotification(List<PrintJobInfo> printJobs) {
+ if (printJobs.size() <= 0) {
+ removeNotification();
+ } else if (printJobs.size() == 1) {
+ createSimpleNotification(printJobs.get(0));
+ } else {
+ createStackedNotification(printJobs);
+ }
+ }
+
+ private void createSimpleNotification(PrintJobInfo printJob) {
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_FAILED: {
+ createFailedNotification(printJob);
+ } break;
+
+ case PrintJobInfo.STATE_BLOCKED: {
+ if (!printJob.isCancelling()) {
+ createBlockedNotification(printJob);
+ } else {
+ createCancellingNotification(printJob);
+ }
+ } break;
+
+ default: {
+ if (!printJob.isCancelling()) {
+ createPrintingNotification(printJob);
+ } else {
+ createCancellingNotification(printJob);
+ }
+ } break;
+ }
+ }
+
+ private void createPrintingNotification(PrintJobInfo printJob) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentIntent(createContentIntent(printJob.getId()))
+ .setSmallIcon(computeNotificationIcon(printJob))
+ .setContentTitle(computeNotificationTitle(printJob))
+ .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
+ createCancelIntent(printJob))
+ .setContentText(printJob.getPrinterName())
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true)
+ .setShowWhen(true);
+ mNotificationManager.notify(0, builder.build());
+ }
+
+ private void createFailedNotification(PrintJobInfo printJob) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentIntent(createContentIntent(printJob.getId()))
+ .setSmallIcon(computeNotificationIcon(printJob))
+ .setContentTitle(computeNotificationTitle(printJob))
+ .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
+ createCancelIntent(printJob))
+ .addAction(R.drawable.ic_restart, mContext.getString(R.string.restart),
+ createRestartIntent(printJob.getId()))
+ .setContentText(printJob.getPrinterName())
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true)
+ .setShowWhen(true);
+ mNotificationManager.notify(0, builder.build());
+ }
+
+ private void createBlockedNotification(PrintJobInfo printJob) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentIntent(createContentIntent(printJob.getId()))
+ .setSmallIcon(computeNotificationIcon(printJob))
+ .setContentTitle(computeNotificationTitle(printJob))
+ .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel),
+ createCancelIntent(printJob))
+ .setContentText(printJob.getPrinterName())
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true)
+ .setShowWhen(true);
+ mNotificationManager.notify(0, builder.build());
+ }
+
+ private void createCancellingNotification(PrintJobInfo printJob) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentIntent(createContentIntent(printJob.getId()))
+ .setSmallIcon(computeNotificationIcon(printJob))
+ .setContentTitle(computeNotificationTitle(printJob))
+ .setContentText(printJob.getPrinterName())
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true)
+ .setShowWhen(true);
+ mNotificationManager.notify(0, builder.build());
+ }
+
+ private void createStackedNotification(List<PrintJobInfo> printJobs) {
+ Notification.Builder builder = new Notification.Builder(mContext)
+ .setContentIntent(createContentIntent(null))
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true)
+ .setShowWhen(true);
+
+ final int printJobCount = printJobs.size();
+
+ InboxStyle inboxStyle = new InboxStyle();
+ inboxStyle.setBigContentTitle(String.format(mContext.getResources().getQuantityText(
+ R.plurals.composite_notification_title_template,
+ printJobCount).toString(), printJobCount));
+
+ for (int i = printJobCount - 1; i>= 0; i--) {
+ PrintJobInfo printJob = printJobs.get(i);
+ if (i == printJobCount - 1) {
+ builder.setLargeIcon(((BitmapDrawable) mContext.getResources().getDrawable(
+ computeNotificationIcon(printJob))).getBitmap());
+ builder.setSmallIcon(computeNotificationIcon(printJob));
+ builder.setContentTitle(computeNotificationTitle(printJob));
+ builder.setContentText(printJob.getPrinterName());
+ }
+ inboxStyle.addLine(computeNotificationTitle(printJob));
+ }
+
+ builder.setNumber(printJobCount);
+ builder.setStyle(inboxStyle);
+
+ mNotificationManager.notify(0, builder.build());
+ }
+
+ private String computeNotificationTitle(PrintJobInfo printJob) {
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_FAILED: {
+ return mContext.getString(R.string.failed_notification_title_template,
+ printJob.getLabel());
+ }
+
+ case PrintJobInfo.STATE_BLOCKED: {
+ if (!printJob.isCancelling()) {
+ return mContext.getString(R.string.blocked_notification_title_template,
+ printJob.getLabel());
+ } else {
+ return mContext.getString(
+ R.string.cancelling_notification_title_template,
+ printJob.getLabel());
+ }
+ }
+
+ default: {
+ if (!printJob.isCancelling()) {
+ return mContext.getString(R.string.printing_notification_title_template,
+ printJob.getLabel());
+ } else {
+ return mContext.getString(
+ R.string.cancelling_notification_title_template,
+ printJob.getLabel());
+ }
+ }
+ }
+ }
+
+ private void removeNotification() {
+ mNotificationManager.cancel(0);
+ }
+
+ private PendingIntent createContentIntent(PrintJobId printJobId) {
+ Intent intent = new Intent(Settings.ACTION_PRINT_SETTINGS);
+ if (printJobId != null) {
+ intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId.flattenToString());
+ intent.setData(Uri.fromParts("printjob", printJobId.flattenToString(), null));
+ }
+ return PendingIntent.getActivity(mContext, 0, intent, 0);
+ }
+
+ private PendingIntent createCancelIntent(PrintJobInfo printJob) {
+ Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
+ intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + printJob.getId().flattenToString());
+ intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId());
+ intent.putExtra(EXTRA_PRINTJOB_LABEL, printJob.getLabel());
+ intent.putExtra(EXTRA_PRINTER_NAME, printJob.getPrinterName());
+ return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ }
+
+ private PendingIntent createRestartIntent(PrintJobId printJobId) {
+ Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
+ intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + printJobId.flattenToString());
+ intent.putExtra(EXTRA_PRINT_JOB_ID, printJobId);
+ return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+ }
+
+ private static boolean shouldNotifyForState(int state) {
+ switch (state) {
+ case PrintJobInfo.STATE_QUEUED:
+ case PrintJobInfo.STATE_STARTED:
+ case PrintJobInfo.STATE_FAILED:
+ case PrintJobInfo.STATE_COMPLETED:
+ case PrintJobInfo.STATE_CANCELED:
+ case PrintJobInfo.STATE_BLOCKED: {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static int computeNotificationIcon(PrintJobInfo printJob) {
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_FAILED:
+ case PrintJobInfo.STATE_BLOCKED: {
+ return com.android.internal.R.drawable.ic_print_error;
+ }
+ default: {
+ if (!printJob.isCancelling()) {
+ return com.android.internal.R.drawable.ic_print;
+ } else {
+ return R.drawable.stat_notify_cancelling;
+ }
+ }
+ }
+ }
+
+ public static final class NotificationBroadcastReceiver extends BroadcastReceiver {
+ private static final String LOG_TAG = "NotificationBroadcastReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action != null && action.startsWith(INTENT_ACTION_CANCEL_PRINTJOB)) {
+ PrintJobId printJobId = intent.getExtras().getParcelable(EXTRA_PRINT_JOB_ID);
+ String printJobLabel = intent.getExtras().getString(EXTRA_PRINTJOB_LABEL);
+ String printerName = intent.getExtras().getString(EXTRA_PRINTER_NAME);
+ handleCancelPrintJob(context, printJobId, printJobLabel, printerName);
+ } else if (action != null && action.startsWith(INTENT_ACTION_RESTART_PRINTJOB)) {
+ PrintJobId printJobId = intent.getExtras().getParcelable(EXTRA_PRINT_JOB_ID);
+ handleRestartPrintJob(context, printJobId);
+ }
+ }
+
+ private void handleCancelPrintJob(final Context context, final PrintJobId printJobId,
+ final String printJobLabel, final String printerName) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "handleCancelPrintJob() printJobId:" + printJobId);
+ }
+
+ // Call into the print manager service off the main thread since
+ // the print manager service may end up binding to the print spooler
+ // service which binding is handled on the main thread.
+ PowerManager powerManager = (PowerManager)
+ context.getSystemService(Context.POWER_SERVICE);
+ final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ LOG_TAG);
+ wakeLock.acquire();
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // We need to request the cancellation to be done by the print
+ // manager service since it has to communicate with the managing
+ // print service to request the cancellation. Also we need the
+ // system service to be bound to the spooler since canceling a
+ // print job will trigger persistence of current jobs which is
+ // done on another thread and until it finishes the spooler has
+ // to be kept around.
+ try {
+ IPrintManager printManager = IPrintManager.Stub.asInterface(
+ ServiceManager.getService(Context.PRINT_SERVICE));
+ printManager.cancelPrintJob(printJobId, PrintManager.APP_ID_ANY,
+ UserHandle.myUserId());
+ } catch (RemoteException re) {
+ Log.i(LOG_TAG, "Error requestion print job cancellation", re);
+ } finally {
+ wakeLock.release();
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+
+ private void handleRestartPrintJob(final Context context, final PrintJobId printJobId) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "handleRestartPrintJob() printJobId:" + printJobId);
+ }
+
+ // Call into the print manager service off the main thread since
+ // the print manager service may end up binding to the print spooler
+ // service which binding is handled on the main thread.
+ PowerManager powerManager = (PowerManager)
+ context.getSystemService(Context.POWER_SERVICE);
+ final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ LOG_TAG);
+ wakeLock.acquire();
+
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // We need to request the restart to be done by the print manager
+ // service since the latter must be bound to the spooler because
+ // restarting a print job will trigger persistence of current jobs
+ // which is done on another thread and until it finishes the spooler has
+ // to be kept around.
+ try {
+ IPrintManager printManager = IPrintManager.Stub.asInterface(
+ ServiceManager.getService(Context.PRINT_SERVICE));
+ printManager.restartPrintJob(printJobId, PrintManager.APP_ID_ANY,
+ UserHandle.myUserId());
+ } catch (RemoteException re) {
+ Log.i(LOG_TAG, "Error requestion print job restart", re);
+ } finally {
+ wakeLock.release();
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerProvider.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerProvider.java
new file mode 100644
index 0000000..06723c3
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerProvider.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler.model;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+
+public class PrintSpoolerProvider implements ServiceConnection {
+ private final Context mContext;
+ private final Runnable mCallback;
+
+ private PrintSpoolerService mSpooler;
+
+ public PrintSpoolerProvider(Context context, Runnable callback) {
+ mContext = context;
+ mCallback = callback;
+ Intent intent = new Intent(mContext, PrintSpoolerService.class);
+ mContext.bindService(intent, this, 0);
+ }
+
+ public PrintSpoolerService getSpooler() {
+ return mSpooler;
+ }
+
+ public void destroy() {
+ if (mSpooler != null) {
+ mContext.unbindService(this);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mSpooler = ((PrintSpoolerService.PrintSpooler) service).getService();
+ if (mSpooler != null) {
+ mCallback.run();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ /* do nothing - we are in the same process */
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
new file mode 100644
index 0000000..045a2f9
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -0,0 +1,1300 @@
+/*
+ * Copyright (C) 2013 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.printspooler.model;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.IPrintSpooler;
+import android.print.IPrintSpoolerCallbacks;
+import android.print.IPrintSpoolerClient;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentInfo;
+import android.print.PrintJobId;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.printspooler.R;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+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;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Service for exposing some of the {@link PrintSpooler} functionality to
+ * another process.
+ */
+public final class PrintSpoolerService extends Service {
+
+ private static final String LOG_TAG = "PrintSpoolerService";
+
+ private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
+
+ private static final boolean DEBUG_PERSISTENCE = false;
+
+ private static final boolean PERSISTNECE_MANAGER_ENABLED = true;
+
+ private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000;
+
+ private static final String PRINT_JOB_FILE_PREFIX = "print_job_";
+
+ private static final String PRINT_FILE_EXTENSION = "pdf";
+
+ private static final Object sLock = new Object();
+
+ private final Object mLock = new Object();
+
+ private final List<PrintJobInfo> mPrintJobs = new ArrayList<>();
+
+ private static PrintSpoolerService sInstance;
+
+ private IPrintSpoolerClient mClient;
+
+ private HandlerCaller mHandlerCaller;
+
+ private PersistenceManager mPersistanceManager;
+
+ private NotificationController mNotificationController;
+
+ public static PrintSpoolerService peekInstance() {
+ synchronized (sLock) {
+ return sInstance;
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandlerCaller = new HandlerCaller(this, getMainLooper(),
+ new HandlerCallerCallback(), false);
+
+ mPersistanceManager = new PersistenceManager();
+ mNotificationController = new NotificationController(PrintSpoolerService.this);
+
+ synchronized (mLock) {
+ mPersistanceManager.readStateLocked();
+ handleReadPrintJobsLocked();
+ }
+
+ synchronized (sLock) {
+ sInstance = this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new PrintSpooler();
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ synchronized (mLock) {
+ String prefix = (args.length > 0) ? args[0] : "";
+ String tab = " ";
+
+ pw.append(prefix).append("print jobs:").println();
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ pw.append(prefix).append(tab).append(printJob.toString());
+ pw.println();
+ }
+
+ pw.append(prefix).append("print job files:").println();
+ File[] files = getFilesDir().listFiles();
+ if (files != null) {
+ final int fileCount = files.length;
+ for (int i = 0; i < fileCount; i++) {
+ File file = files[i];
+ if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
+ pw.append(prefix).append(tab).append(file.getName()).println();
+ }
+ }
+ }
+ }
+ }
+
+ private void sendOnPrintJobQueued(PrintJobInfo printJob) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ private void sendOnAllPrintJobsHandled() {
+ Message message = mHandlerCaller.obtainMessage(
+ HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ private final class HandlerCallerCallback implements HandlerCaller.Callback {
+ public static final int MSG_SET_CLIENT = 1;
+ public static final int MSG_ON_PRINT_JOB_QUEUED = 2;
+ public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 3;
+ public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 4;
+ public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 5;
+ public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 6;
+
+ @Override
+ public void executeMessage(Message message) {
+ switch (message.what) {
+ case MSG_SET_CLIENT: {
+ synchronized (mLock) {
+ mClient = (IPrintSpoolerClient) message.obj;
+ if (mClient != null) {
+ Message msg = mHandlerCaller.obtainMessage(
+ HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
+ mHandlerCaller.sendMessageDelayed(msg,
+ CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
+ }
+ }
+ } break;
+
+ case MSG_ON_PRINT_JOB_QUEUED: {
+ PrintJobInfo printJob = (PrintJobInfo) message.obj;
+ if (mClient != null) {
+ try {
+ mClient.onPrintJobQueued(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
+ }
+ }
+ } break;
+
+ case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
+ ComponentName service = (ComponentName) message.obj;
+ if (mClient != null) {
+ try {
+ mClient.onAllPrintJobsForServiceHandled(service);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print jobs per service"
+ + " handled.", re);
+ }
+ }
+ } break;
+
+ case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
+ if (mClient != null) {
+ try {
+ mClient.onAllPrintJobsHandled();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
+ }
+ }
+ } break;
+
+ case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
+ checkAllPrintJobsHandled();
+ } break;
+
+ case MSG_ON_PRINT_JOB_STATE_CHANGED: {
+ if (mClient != null) {
+ PrintJobInfo printJob = (PrintJobInfo) message.obj;
+ try {
+ mClient.onPrintJobStateChanged(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for print job state change.", re);
+ }
+ }
+ } break;
+ }
+ }
+ }
+
+ public List<PrintJobInfo> getPrintJobInfos(ComponentName componentName,
+ int state, int appId) {
+ List<PrintJobInfo> foundPrintJobs = null;
+ synchronized (mLock) {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ PrinterId printerId = printJob.getPrinterId();
+ final boolean sameComponent = (componentName == null
+ || (printerId != null
+ && componentName.equals(printerId.getServiceName())));
+ final boolean sameAppId = appId == PrintManager.APP_ID_ANY
+ || printJob.getAppId() == appId;
+ final boolean sameState = (state == printJob.getState())
+ || (state == PrintJobInfo.STATE_ANY)
+ || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
+ && isStateVisibleToUser(printJob.getState()))
+ || (state == PrintJobInfo.STATE_ANY_ACTIVE
+ && isActiveState(printJob.getState()))
+ || (state == PrintJobInfo.STATE_ANY_SCHEDULED
+ && isScheduledState(printJob.getState()));
+ if (sameComponent && sameAppId && sameState) {
+ if (foundPrintJobs == null) {
+ foundPrintJobs = new ArrayList<>();
+ }
+ foundPrintJobs.add(printJob);
+ }
+ }
+ }
+ return foundPrintJobs;
+ }
+
+ private boolean isStateVisibleToUser(int state) {
+ return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED
+ || state == PrintJobInfo.STATE_COMPLETED || state == PrintJobInfo.STATE_CANCELED
+ || state == PrintJobInfo.STATE_BLOCKED));
+ }
+
+ public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) {
+ synchronized (mLock) {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ if (printJob.getId().equals(printJobId)
+ && (appId == PrintManager.APP_ID_ANY
+ || appId == printJob.getAppId())) {
+ return printJob;
+ }
+ }
+ return null;
+ }
+ }
+
+ public void createPrintJob(PrintJobInfo printJob) {
+ synchronized (mLock) {
+ addPrintJobLocked(printJob);
+ setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
+
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
+ printJob);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+ }
+
+ private void handleReadPrintJobsLocked() {
+ // Make a map with the files for a print job since we may have
+ // to delete some. One example of getting orphan files if the
+ // spooler crashes while constructing a print job. We do not
+ // persist partially populated print jobs under construction to
+ // avoid special handling for various attributes missing.
+ ArrayMap<PrintJobId, File> fileForJobMap = null;
+ File[] files = getFilesDir().listFiles();
+ if (files != null) {
+ final int fileCount = files.length;
+ for (int i = 0; i < fileCount; i++) {
+ File file = files[i];
+ if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) {
+ if (fileForJobMap == null) {
+ fileForJobMap = new ArrayMap<PrintJobId, File>();
+ }
+ String printJobIdString = file.getName().substring(
+ PRINT_JOB_FILE_PREFIX.length(),
+ file.getName().indexOf('.'));
+ PrintJobId printJobId = PrintJobId.unflattenFromString(
+ printJobIdString);
+ fileForJobMap.put(printJobId, file);
+ }
+ }
+ }
+
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+
+ // We want to have only the orphan files at the end.
+ if (fileForJobMap != null) {
+ fileForJobMap.remove(printJob.getId());
+ }
+
+ switch (printJob.getState()) {
+ case PrintJobInfo.STATE_QUEUED:
+ case PrintJobInfo.STATE_STARTED:
+ case PrintJobInfo.STATE_BLOCKED: {
+ // We have a print job that was queued or started or blocked in
+ // the past but the device battery died or a crash occurred. In
+ // this case we assume the print job failed and let the user
+ // decide whether to restart the job or just cancel it.
+ setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED,
+ getString(R.string.no_connection_to_printer));
+ } break;
+ }
+ }
+
+ if (!mPrintJobs.isEmpty()) {
+ // Update the notification.
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+ }
+
+ // Delete the orphan files.
+ if (fileForJobMap != null) {
+ final int orphanFileCount = fileForJobMap.size();
+ for (int i = 0; i < orphanFileCount; i++) {
+ File file = fileForJobMap.valueAt(i);
+ file.delete();
+ }
+ }
+ }
+
+ public void checkAllPrintJobsHandled() {
+ synchronized (mLock) {
+ if (!hasActivePrintJobsLocked()) {
+ notifyOnAllPrintJobsHandled();
+ }
+ }
+ }
+
+ public void writePrintJobData(final ParcelFileDescriptor fd, final PrintJobId printJobId) {
+ final PrintJobInfo printJob;
+ synchronized (mLock) {
+ printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+ }
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ FileInputStream in = null;
+ FileOutputStream out = null;
+ try {
+ if (printJob != null) {
+ File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
+ in = new FileInputStream(file);
+ out = new FileOutputStream(fd.getFileDescriptor());
+ }
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ return null;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ } catch (FileNotFoundException fnfe) {
+ Log.e(LOG_TAG, "Error writing print job data!", fnfe);
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error writing print job data!", ioe);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(fd);
+ }
+ Log.i(LOG_TAG, "[END WRITE]");
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+
+ public static File generateFileForPrintJob(Context context, PrintJobId printJobId) {
+ return new File(context.getFilesDir(), PRINT_JOB_FILE_PREFIX
+ + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION);
+ }
+
+ private void addPrintJobLocked(PrintJobInfo printJob) {
+ mPrintJobs.add(printJob);
+ if (DEBUG_PRINT_JOB_LIFECYCLE) {
+ Slog.i(LOG_TAG, "[ADD] " + printJob);
+ }
+ }
+
+ private void removeObsoletePrintJobs() {
+ synchronized (mLock) {
+ boolean persistState = false;
+ final int printJobCount = mPrintJobs.size();
+ for (int i = printJobCount - 1; i >= 0; i--) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ if (isObsoleteState(printJob.getState())) {
+ mPrintJobs.remove(i);
+ if (DEBUG_PRINT_JOB_LIFECYCLE) {
+ Slog.i(LOG_TAG, "[REMOVE] " + printJob.getId().flattenToString());
+ }
+ removePrintJobFileLocked(printJob.getId());
+ persistState = true;
+ }
+ }
+ if (persistState) {
+ mPersistanceManager.writeStateLocked();
+ }
+ }
+ }
+
+ private void removePrintJobFileLocked(PrintJobId printJobId) {
+ File file = generateFileForPrintJob(PrintSpoolerService.this, printJobId);
+ if (file.exists()) {
+ file.delete();
+ if (DEBUG_PRINT_JOB_LIFECYCLE) {
+ Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId);
+ }
+ }
+ }
+
+ public boolean setPrintJobState(PrintJobId printJobId, int state, String error) {
+ boolean success = false;
+
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ final int oldState = printJob.getState();
+ if (oldState == state) {
+ return false;
+ }
+
+ success = true;
+
+ printJob.setState(state);
+ printJob.setStateReason(error);
+ printJob.setCancelling(false);
+
+ if (DEBUG_PRINT_JOB_LIFECYCLE) {
+ Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
+ }
+
+ switch (state) {
+ case PrintJobInfo.STATE_COMPLETED:
+ case PrintJobInfo.STATE_CANCELED:
+ mPrintJobs.remove(printJob);
+ removePrintJobFileLocked(printJob.getId());
+ // $fall-through$
+
+ case PrintJobInfo.STATE_FAILED: {
+ PrinterId printerId = printJob.getPrinterId();
+ if (printerId != null) {
+ ComponentName service = printerId.getServiceName();
+ if (!hasActivePrintJobsForServiceLocked(service)) {
+ sendOnAllPrintJobsForServiceHandled(service);
+ }
+ }
+ } break;
+
+ case PrintJobInfo.STATE_QUEUED: {
+ sendOnPrintJobQueued(new PrintJobInfo(printJob));
+ } break;
+ }
+
+ if (shouldPersistPrintJob(printJob)) {
+ mPersistanceManager.writeStateLocked();
+ }
+
+ if (!hasActivePrintJobsLocked()) {
+ notifyOnAllPrintJobsHandled();
+ }
+
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
+ printJob);
+ mHandlerCaller.executeOrSendMessage(message);
+
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+ }
+ }
+
+ return success;
+ }
+
+ public boolean hasActivePrintJobsLocked() {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ if (isActiveState(printJob.getState())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo printJob = mPrintJobs.get(i);
+ if (isActiveState(printJob.getState()) && printJob.getPrinterId() != null
+ && printJob.getPrinterId().getServiceName().equals(service)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isObsoleteState(int printJobState) {
+ return (isTerminalState(printJobState)
+ || printJobState == PrintJobInfo.STATE_QUEUED);
+ }
+
+ private boolean isScheduledState(int printJobState) {
+ return printJobState == PrintJobInfo.STATE_QUEUED
+ || printJobState == PrintJobInfo.STATE_STARTED
+ || printJobState == PrintJobInfo.STATE_BLOCKED;
+ }
+
+ private boolean isActiveState(int printJobState) {
+ return printJobState == PrintJobInfo.STATE_CREATED
+ || printJobState == PrintJobInfo.STATE_QUEUED
+ || printJobState == PrintJobInfo.STATE_STARTED
+ || printJobState == PrintJobInfo.STATE_BLOCKED;
+ }
+
+ private boolean isTerminalState(int printJobState) {
+ return printJobState == PrintJobInfo.STATE_COMPLETED
+ || printJobState == PrintJobInfo.STATE_CANCELED;
+ }
+
+ public boolean setPrintJobTag(PrintJobId printJobId, String tag) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ String printJobTag = printJob.getTag();
+ if (printJobTag == null) {
+ if (tag == null) {
+ return false;
+ }
+ } else if (printJobTag.equals(tag)) {
+ return false;
+ }
+ printJob.setTag(tag);
+ if (shouldPersistPrintJob(printJob)) {
+ mPersistanceManager.writeStateLocked();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
+ synchronized (mLock) {
+ PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+ if (printJob != null) {
+ printJob.setCancelling(cancelling);
+ if (shouldPersistPrintJob(printJob)) {
+ mPersistanceManager.writeStateLocked();
+ }
+ mNotificationController.onUpdateNotifications(mPrintJobs);
+
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
+ printJob);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+ }
+ }
+
+ public void updatePrintJobUserConfigurableOptionsNoPersistence(PrintJobInfo printJob) {
+ synchronized (mLock) {
+ final int printJobCount = mPrintJobs.size();
+ for (int i = 0; i < printJobCount; i++) {
+ PrintJobInfo cachedPrintJob = mPrintJobs.get(i);
+ if (cachedPrintJob.getId().equals(printJob.getId())) {
+ cachedPrintJob.setPrinterId(printJob.getPrinterId());
+ cachedPrintJob.setPrinterName(printJob.getPrinterName());
+ cachedPrintJob.setCopies(printJob.getCopies());
+ cachedPrintJob.setDocumentInfo(printJob.getDocumentInfo());
+ cachedPrintJob.setPages(printJob.getPages());
+ cachedPrintJob.setAttributes(printJob.getAttributes());
+ cachedPrintJob.setAdvancedOptions(printJob.getAdvancedOptions());
+ return;
+ }
+ }
+ throw new IllegalArgumentException("No print job with id:" + printJob.getId());
+ }
+ }
+
+ private boolean shouldPersistPrintJob(PrintJobInfo printJob) {
+ return printJob.getState() >= PrintJobInfo.STATE_QUEUED;
+ }
+
+ private void notifyOnAllPrintJobsHandled() {
+ // This has to run on the tread that is persisting the current state
+ // since this call may result in the system unbinding from the spooler
+ // and as a result the spooler process may get killed before the write
+ // completes.
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ sendOnAllPrintJobsHandled();
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ private final class PersistenceManager {
+ private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
+
+ private static final String TAG_SPOOLER = "spooler";
+ private static final String TAG_JOB = "job";
+
+ private static final String TAG_PRINTER_ID = "printerId";
+ private static final String TAG_PAGE_RANGE = "pageRange";
+ private static final String TAG_ATTRIBUTES = "attributes";
+ private static final String TAG_DOCUMENT_INFO = "documentInfo";
+
+ private static final String ATTR_ID = "id";
+ private static final String ATTR_LABEL = "label";
+ private static final String ATTR_LABEL_RES_ID = "labelResId";
+ private static final String ATTR_PACKAGE_NAME = "packageName";
+ private static final String ATTR_STATE = "state";
+ private static final String ATTR_APP_ID = "appId";
+ private static final String ATTR_TAG = "tag";
+ private static final String ATTR_CREATION_TIME = "creationTime";
+ private static final String ATTR_COPIES = "copies";
+ private static final String ATTR_PRINTER_NAME = "printerName";
+ private static final String ATTR_STATE_REASON = "stateReason";
+ private static final String ATTR_CANCELLING = "cancelling";
+
+ private static final String TAG_ADVANCED_OPTIONS = "advancedOptions";
+ private static final String TAG_ADVANCED_OPTION = "advancedOption";
+ private static final String ATTR_KEY = "key";
+ private static final String ATTR_TYPE = "type";
+ private static final String ATTR_VALUE = "value";
+ private static final String TYPE_STRING = "string";
+ private static final String TYPE_INT = "int";
+
+ private static final String TAG_MEDIA_SIZE = "mediaSize";
+ private static final String TAG_RESOLUTION = "resolution";
+ private static final String TAG_MARGINS = "margins";
+
+ private static final String ATTR_COLOR_MODE = "colorMode";
+
+ private static final String ATTR_LOCAL_ID = "localId";
+ private static final String ATTR_SERVICE_NAME = "serviceName";
+
+ private static final String ATTR_WIDTH_MILS = "widthMils";
+ private static final String ATTR_HEIGHT_MILS = "heightMils";
+
+ private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
+ private static final String ATTR_VERTICAL_DPI = "verticalDpi";
+
+ private static final String ATTR_LEFT_MILS = "leftMils";
+ private static final String ATTR_TOP_MILS = "topMils";
+ private static final String ATTR_RIGHT_MILS = "rightMils";
+ private static final String ATTR_BOTTOM_MILS = "bottomMils";
+
+ private static final String ATTR_START = "start";
+ private static final String ATTR_END = "end";
+
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_PAGE_COUNT = "pageCount";
+ private static final String ATTR_CONTENT_TYPE = "contentType";
+ private static final String ATTR_DATA_SIZE = "dataSize";
+
+ private final AtomicFile mStatePersistFile;
+
+ private boolean mWriteStateScheduled;
+
+ private PersistenceManager() {
+ mStatePersistFile = new AtomicFile(new File(getFilesDir(),
+ PERSIST_FILE_NAME));
+ }
+
+ public void writeStateLocked() {
+ if (!PERSISTNECE_MANAGER_ENABLED) {
+ return;
+ }
+ if (mWriteStateScheduled) {
+ return;
+ }
+ mWriteStateScheduled = true;
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ synchronized (mLock) {
+ mWriteStateScheduled = false;
+ doWriteStateLocked();
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ private void doWriteStateLocked() {
+ if (DEBUG_PERSISTENCE) {
+ Log.i(LOG_TAG, "[PERSIST START]");
+ }
+ FileOutputStream out = null;
+ try {
+ out = mStatePersistFile.startWrite();
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_SPOOLER);
+
+ List<PrintJobInfo> printJobs = mPrintJobs;
+
+ final int printJobCount = printJobs.size();
+ for (int j = 0; j < printJobCount; j++) {
+ PrintJobInfo printJob = printJobs.get(j);
+
+ if (!shouldPersistPrintJob(printJob)) {
+ continue;
+ }
+
+ serializer.startTag(null, TAG_JOB);
+
+ serializer.attribute(null, ATTR_ID, printJob.getId().flattenToString());
+ serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
+ serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
+ serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
+ String tag = printJob.getTag();
+ if (tag != null) {
+ serializer.attribute(null, ATTR_TAG, tag);
+ }
+ serializer.attribute(null, ATTR_CREATION_TIME, String.valueOf(
+ printJob.getCreationTime()));
+ serializer.attribute(null, ATTR_COPIES, String.valueOf(printJob.getCopies()));
+ String printerName = printJob.getPrinterName();
+ if (!TextUtils.isEmpty(printerName)) {
+ serializer.attribute(null, ATTR_PRINTER_NAME, printerName);
+ }
+ String stateReason = printJob.getStateReason();
+ if (!TextUtils.isEmpty(stateReason)) {
+ serializer.attribute(null, ATTR_STATE_REASON, stateReason);
+ }
+ serializer.attribute(null, ATTR_CANCELLING, String.valueOf(
+ printJob.isCancelling()));
+
+ PrinterId printerId = printJob.getPrinterId();
+ if (printerId != null) {
+ serializer.startTag(null, TAG_PRINTER_ID);
+ serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
+ serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
+ .flattenToString());
+ serializer.endTag(null, TAG_PRINTER_ID);
+ }
+
+ PageRange[] pages = printJob.getPages();
+ if (pages != null) {
+ for (int i = 0; i < pages.length; i++) {
+ serializer.startTag(null, TAG_PAGE_RANGE);
+ serializer.attribute(null, ATTR_START, String.valueOf(
+ pages[i].getStart()));
+ serializer.attribute(null, ATTR_END, String.valueOf(
+ pages[i].getEnd()));
+ serializer.endTag(null, TAG_PAGE_RANGE);
+ }
+ }
+
+ PrintAttributes attributes = printJob.getAttributes();
+ if (attributes != null) {
+ serializer.startTag(null, TAG_ATTRIBUTES);
+
+ final int colorMode = attributes.getColorMode();
+ serializer.attribute(null, ATTR_COLOR_MODE,
+ String.valueOf(colorMode));
+
+ MediaSize mediaSize = attributes.getMediaSize();
+ if (mediaSize != null) {
+ serializer.startTag(null, TAG_MEDIA_SIZE);
+ serializer.attribute(null, ATTR_ID, mediaSize.getId());
+ serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
+ mediaSize.getWidthMils()));
+ serializer.attribute(null, ATTR_HEIGHT_MILS, String.valueOf(
+ mediaSize.getHeightMils()));
+ // We prefer to store only the package name and
+ // resource id and fallback to the label.
+ if (!TextUtils.isEmpty(mediaSize.mPackageName)
+ && mediaSize.mLabelResId > 0) {
+ serializer.attribute(null, ATTR_PACKAGE_NAME,
+ mediaSize.mPackageName);
+ serializer.attribute(null, ATTR_LABEL_RES_ID,
+ String.valueOf(mediaSize.mLabelResId));
+ } else {
+ serializer.attribute(null, ATTR_LABEL,
+ mediaSize.getLabel(getPackageManager()));
+ }
+ serializer.endTag(null, TAG_MEDIA_SIZE);
+ }
+
+ Resolution resolution = attributes.getResolution();
+ if (resolution != null) {
+ serializer.startTag(null, TAG_RESOLUTION);
+ serializer.attribute(null, ATTR_ID, resolution.getId());
+ serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
+ resolution.getHorizontalDpi()));
+ serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
+ resolution.getVerticalDpi()));
+ serializer.attribute(null, ATTR_LABEL,
+ resolution.getLabel());
+ serializer.endTag(null, TAG_RESOLUTION);
+ }
+
+ Margins margins = attributes.getMinMargins();
+ if (margins != null) {
+ serializer.startTag(null, TAG_MARGINS);
+ serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
+ margins.getLeftMils()));
+ serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
+ margins.getTopMils()));
+ serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
+ margins.getRightMils()));
+ serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
+ margins.getBottomMils()));
+ serializer.endTag(null, TAG_MARGINS);
+ }
+
+ serializer.endTag(null, TAG_ATTRIBUTES);
+ }
+
+ PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
+ if (documentInfo != null) {
+ serializer.startTag(null, TAG_DOCUMENT_INFO);
+ serializer.attribute(null, ATTR_NAME, documentInfo.getName());
+ serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
+ documentInfo.getContentType()));
+ serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
+ documentInfo.getPageCount()));
+ serializer.attribute(null, ATTR_DATA_SIZE, String.valueOf(
+ documentInfo.getDataSize()));
+ serializer.endTag(null, TAG_DOCUMENT_INFO);
+ }
+
+ Bundle advancedOptions = printJob.getAdvancedOptions();
+ if (advancedOptions != null) {
+ serializer.startTag(null, TAG_ADVANCED_OPTIONS);
+ for (String key : advancedOptions.keySet()) {
+ Object value = advancedOptions.get(key);
+ if (value instanceof String) {
+ String stringValue = (String) value;
+ serializer.startTag(null, TAG_ADVANCED_OPTION);
+ serializer.attribute(null, ATTR_KEY, key);
+ serializer.attribute(null, ATTR_TYPE, TYPE_STRING);
+ serializer.attribute(null, ATTR_VALUE, stringValue);
+ serializer.endTag(null, TAG_ADVANCED_OPTION);
+ } else if (value instanceof Integer) {
+ String intValue = Integer.toString((Integer) value);
+ serializer.startTag(null, TAG_ADVANCED_OPTION);
+ serializer.attribute(null, ATTR_KEY, key);
+ serializer.attribute(null, ATTR_TYPE, TYPE_INT);
+ serializer.attribute(null, ATTR_VALUE, intValue);
+ serializer.endTag(null, TAG_ADVANCED_OPTION);
+ }
+ }
+ serializer.endTag(null, TAG_ADVANCED_OPTIONS);
+ }
+
+ serializer.endTag(null, TAG_JOB);
+
+ if (DEBUG_PERSISTENCE) {
+ Log.i(LOG_TAG, "[PERSISTED] " + printJob);
+ }
+ }
+
+ serializer.endTag(null, TAG_SPOOLER);
+ serializer.endDocument();
+ mStatePersistFile.finishWrite(out);
+ if (DEBUG_PERSISTENCE) {
+ Log.i(LOG_TAG, "[PERSIST END]");
+ }
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Failed to write state, restoring backup.", e);
+ mStatePersistFile.failWrite(out);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ public void readStateLocked() {
+ if (!PERSISTNECE_MANAGER_ENABLED) {
+ return;
+ }
+ FileInputStream in = null;
+ try {
+ in = mStatePersistFile.openRead();
+ } catch (FileNotFoundException e) {
+ Log.i(LOG_TAG, "No existing print spooler state.");
+ return;
+ }
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseState(parser);
+ } catch (IllegalStateException ise) {
+ Slog.w(LOG_TAG, "Failed parsing ", ise);
+ } catch (NullPointerException npe) {
+ Slog.w(LOG_TAG, "Failed parsing ", npe);
+ } catch (NumberFormatException nfe) {
+ Slog.w(LOG_TAG, "Failed parsing ", nfe);
+ } catch (XmlPullParserException xppe) {
+ Slog.w(LOG_TAG, "Failed parsing ", xppe);
+ } catch (IOException ioe) {
+ Slog.w(LOG_TAG, "Failed parsing ", ioe);
+ } catch (IndexOutOfBoundsException iobe) {
+ Slog.w(LOG_TAG, "Failed parsing ", iobe);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void parseState(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_SPOOLER);
+ parser.next();
+
+ while (parsePrintJob(parser)) {
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_SPOOLER);
+ }
+
+ private boolean parsePrintJob(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
+ return false;
+ }
+
+ PrintJobInfo printJob = new PrintJobInfo();
+
+ PrintJobId printJobId = PrintJobId.unflattenFromString(
+ parser.getAttributeValue(null, ATTR_ID));
+ printJob.setId(printJobId);
+ String label = parser.getAttributeValue(null, ATTR_LABEL);
+ printJob.setLabel(label);
+ final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
+ printJob.setState(state);
+ final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
+ printJob.setAppId(appId);
+ String tag = parser.getAttributeValue(null, ATTR_TAG);
+ printJob.setTag(tag);
+ String creationTime = parser.getAttributeValue(null, ATTR_CREATION_TIME);
+ printJob.setCreationTime(Long.parseLong(creationTime));
+ String copies = parser.getAttributeValue(null, ATTR_COPIES);
+ printJob.setCopies(Integer.parseInt(copies));
+ String printerName = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
+ printJob.setPrinterName(printerName);
+ String stateReason = parser.getAttributeValue(null, ATTR_STATE_REASON);
+ printJob.setStateReason(stateReason);
+ String cancelling = parser.getAttributeValue(null, ATTR_CANCELLING);
+ printJob.setCancelling(!TextUtils.isEmpty(cancelling)
+ ? Boolean.parseBoolean(cancelling) : false);
+
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
+ String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
+ ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
+ null, ATTR_SERVICE_NAME));
+ printJob.setPrinterId(new PrinterId(service, localId));
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ List<PageRange> pageRanges = null;
+ while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
+ final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
+ final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
+ PageRange pageRange = new PageRange(start, end);
+ if (pageRanges == null) {
+ pageRanges = new ArrayList<PageRange>();
+ }
+ pageRanges.add(pageRange);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
+ parser.next();
+ skipEmptyTextTags(parser);
+ }
+ if (pageRanges != null) {
+ PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
+ pageRanges.toArray(pageRangesArray);
+ printJob.setPages(pageRangesArray);
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
+
+ PrintAttributes.Builder builder = new PrintAttributes.Builder();
+
+ String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
+ builder.setColorMode(Integer.parseInt(colorMode));
+
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ label = parser.getAttributeValue(null, ATTR_LABEL);
+ final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_WIDTH_MILS));
+ final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_HEIGHT_MILS));
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ String labelResIdString = parser.getAttributeValue(null, ATTR_LABEL_RES_ID);
+ final int labelResId = (labelResIdString != null)
+ ? Integer.parseInt(labelResIdString) : 0;
+ label = parser.getAttributeValue(null, ATTR_LABEL);
+ MediaSize mediaSize = new MediaSize(id, label, packageName,
+ widthMils, heightMils, labelResId);
+ builder.setMediaSize(mediaSize);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
+ String id = parser.getAttributeValue(null, ATTR_ID);
+ label = parser.getAttributeValue(null, ATTR_LABEL);
+ final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_HORIZONTAL_DPI));
+ final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_VERTICAL_DPI));
+ Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
+ builder.setResolution(resolution);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
+ final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_LEFT_MILS));
+ final int topMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_TOP_MILS));
+ final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_RIGHT_MILS));
+ final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_BOTTOM_MILS));
+ Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
+ builder.setMinMargins(margins);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
+ parser.next();
+ }
+
+ printJob.setAttributes(builder.build());
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_PAGE_COUNT));
+ final int contentType = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_CONTENT_TYPE));
+ final int dataSize = Integer.parseInt(parser.getAttributeValue(null,
+ ATTR_DATA_SIZE));
+ PrintDocumentInfo info = new PrintDocumentInfo.Builder(name)
+ .setPageCount(pageCount)
+ .setContentType(contentType).build();
+ printJob.setDocumentInfo(info);
+ info.setDataSize(dataSize);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ if (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTIONS)) {
+ parser.next();
+ skipEmptyTextTags(parser);
+ Bundle advancedOptions = new Bundle();
+ while (accept(parser, XmlPullParser.START_TAG, TAG_ADVANCED_OPTION)) {
+ String key = parser.getAttributeValue(null, ATTR_KEY);
+ String value = parser.getAttributeValue(null, ATTR_VALUE);
+ String type = parser.getAttributeValue(null, ATTR_TYPE);
+ if (TYPE_STRING.equals(type)) {
+ advancedOptions.putString(key, value);
+ } else if (TYPE_INT.equals(type)) {
+ advancedOptions.putInt(key, Integer.valueOf(value));
+ }
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTION);
+ parser.next();
+ skipEmptyTextTags(parser);
+ }
+ printJob.setAdvancedOptions(advancedOptions);
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_ADVANCED_OPTIONS);
+ parser.next();
+ }
+
+ mPrintJobs.add(printJob);
+
+ if (DEBUG_PERSISTENCE) {
+ Log.i(LOG_TAG, "[RESTORED] " + printJob);
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_JOB);
+
+ return true;
+ }
+
+ private void expect(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (!accept(parser, type, tag)) {
+ throw new XmlPullParserException("Exepected event: " + type
+ + " and tag: " + tag + " but got event: " + parser.getEventType()
+ + " and tag:" + parser.getName());
+ }
+ }
+
+ private void skipEmptyTextTags(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ while (accept(parser, XmlPullParser.TEXT, null)
+ && "\n".equals(parser.getText())) {
+ parser.next();
+ }
+ }
+
+ private boolean accept(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (parser.getEventType() != type) {
+ return false;
+ }
+ if (tag != null) {
+ if (!tag.equals(parser.getName())) {
+ return false;
+ }
+ } else if (parser.getName() != null) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public final class PrintSpooler extends IPrintSpooler.Stub {
+ @Override
+ public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
+ ComponentName componentName, int state, int appId, int sequence)
+ throws RemoteException {
+ List<PrintJobInfo> printJobs = null;
+ try {
+ printJobs = PrintSpoolerService.this.getPrintJobInfos(
+ componentName, state, appId);
+ } finally {
+ callback.onGetPrintJobInfosResult(printJobs, sequence);
+ }
+ }
+
+ @Override
+ public void getPrintJobInfo(PrintJobId printJobId, IPrintSpoolerCallbacks callback,
+ int appId, int sequence) throws RemoteException {
+ PrintJobInfo printJob = null;
+ try {
+ printJob = PrintSpoolerService.this.getPrintJobInfo(printJobId, appId);
+ } finally {
+ callback.onGetPrintJobInfoResult(printJob, sequence);
+ }
+ }
+
+ @Override
+ public void createPrintJob(PrintJobInfo printJob) {
+ PrintSpoolerService.this.createPrintJob(printJob);
+ }
+
+ @Override
+ public void setPrintJobState(PrintJobId printJobId, int state, String error,
+ IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
+ boolean success = false;
+ try {
+ success = PrintSpoolerService.this.setPrintJobState(
+ printJobId, state, error);
+ } finally {
+ callback.onSetPrintJobStateResult(success, sequece);
+ }
+ }
+
+ @Override
+ public void setPrintJobTag(PrintJobId printJobId, String tag,
+ IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
+ boolean success = false;
+ try {
+ success = PrintSpoolerService.this.setPrintJobTag(printJobId, tag);
+ } finally {
+ callback.onSetPrintJobTagResult(success, sequece);
+ }
+ }
+
+ @Override
+ public void writePrintJobData(ParcelFileDescriptor fd, PrintJobId printJobId) {
+ PrintSpoolerService.this.writePrintJobData(fd, printJobId);
+ }
+
+ @Override
+ public void setClient(IPrintSpoolerClient client) {
+ Message message = mHandlerCaller.obtainMessageO(
+ HandlerCallerCallback.MSG_SET_CLIENT, client);
+ mHandlerCaller.executeOrSendMessage(message);
+ }
+
+ @Override
+ public void removeObsoletePrintJobs() {
+ PrintSpoolerService.this.removeObsoletePrintJobs();
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ PrintSpoolerService.this.dump(fd, writer, args);
+ }
+
+ @Override
+ public void setPrintJobCancelling(PrintJobId printJobId, boolean cancelling) {
+ PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
+ }
+
+ public PrintSpoolerService getService() {
+ return PrintSpoolerService.this;
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
new file mode 100644
index 0000000..e70c361
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -0,0 +1,1148 @@
+/*
+ * 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.printspooler.model;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder.DeathRecipient;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.ILayoutResultCallback;
+import android.print.IPrintDocumentAdapter;
+import android.print.IPrintDocumentAdapterObserver;
+import android.print.IWriteResultCallback;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentInfo;
+import android.util.Log;
+
+import com.android.printspooler.R;
+import com.android.printspooler.util.PageRangeUtils;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+
+public final class RemotePrintDocument {
+ private static final String LOG_TAG = "RemotePrintDocument";
+
+ private static final boolean DEBUG = false;
+
+ private static final int STATE_INITIAL = 0;
+ private static final int STATE_STARTED = 1;
+ private static final int STATE_UPDATING = 2;
+ private static final int STATE_UPDATED = 3;
+ private static final int STATE_FAILED = 4;
+ private static final int STATE_FINISHED = 5;
+ private static final int STATE_CANCELING = 6;
+ private static final int STATE_CANCELED = 7;
+ private static final int STATE_DESTROYED = 8;
+
+ private static final PageRange[] ALL_PAGES_ARRAY = new PageRange[] {
+ PageRange.ALL_PAGES
+ };
+
+ private final Context mContext;
+
+ private final RemotePrintDocumentInfo mDocumentInfo;
+ private final UpdateSpec mUpdateSpec = new UpdateSpec();
+
+ private final Looper mLooper;
+ private final IPrintDocumentAdapter mPrintDocumentAdapter;
+ private final DocumentObserver mDocumentObserver;
+
+ private final UpdateResultCallbacks mUpdateCallbacks;
+
+ private final CommandDoneCallback mCommandResultCallback =
+ new CommandDoneCallback() {
+ @Override
+ public void onDone() {
+ if (mCurrentCommand.isCompleted()) {
+ if (mCurrentCommand instanceof LayoutCommand) {
+ // If there is a next command after a layout is done, then another
+ // update was issued and the next command is another layout, so we
+ // do nothing. However, if there is no next command we may need to
+ // ask for some pages given we do not already have them or we do
+ // but the content has changed.
+ LayoutCommand layoutCommand = (LayoutCommand) mCurrentCommand;
+ if (mNextCommand == null) {
+ if (layoutCommand.isDocumentChanged() || !PageRangeUtils.contains(
+ mDocumentInfo.writtenPages, mUpdateSpec.pages)) {
+ mNextCommand = new WriteCommand(mContext, mLooper,
+ mPrintDocumentAdapter, mDocumentInfo,
+ mDocumentInfo.info.getPageCount(), mUpdateSpec.pages,
+ mDocumentInfo.file, mCommandResultCallback);
+ } else {
+ // If we have the requested pages just update that ones to be printed.
+ mDocumentInfo.printedPages = computePrintedPages(mUpdateSpec.pages,
+ mDocumentInfo.writtenPages, mDocumentInfo.info.getPageCount());
+ // Notify we are done.
+ notifyUpdateCompleted();
+ }
+ }
+ } else {
+ // We always notify after a write.
+ notifyUpdateCompleted();
+ }
+ runPendingCommand();
+ } else if (mCurrentCommand.isFailed()) {
+ mState = STATE_FAILED;
+ CharSequence error = mCurrentCommand.getError();
+ mCurrentCommand = null;
+ mNextCommand = null;
+ mUpdateSpec.reset();
+ notifyUpdateFailed(error);
+ } else if (mCurrentCommand.isCanceled()) {
+ if (mState == STATE_CANCELING) {
+ mState = STATE_CANCELED;
+ notifyUpdateCanceled();
+ }
+ runPendingCommand();
+ }
+ }
+ };
+
+ private final DeathRecipient mDeathRecipient = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ finish();
+ }
+ };
+
+ private int mState = STATE_INITIAL;
+
+ private AsyncCommand mCurrentCommand;
+ private AsyncCommand mNextCommand;
+
+ public interface DocumentObserver {
+ public void onDestroy();
+ }
+
+ public interface UpdateResultCallbacks {
+ public void onUpdateCompleted(RemotePrintDocumentInfo document);
+ public void onUpdateCanceled();
+ public void onUpdateFailed(CharSequence error);
+ }
+
+ public RemotePrintDocument(Context context, IPrintDocumentAdapter adapter,
+ File file, DocumentObserver destroyListener, UpdateResultCallbacks callbacks) {
+ mPrintDocumentAdapter = adapter;
+ mLooper = context.getMainLooper();
+ mContext = context;
+ mDocumentObserver = destroyListener;
+ mDocumentInfo = new RemotePrintDocumentInfo();
+ mDocumentInfo.file = file;
+ mUpdateCallbacks = callbacks;
+ connectToRemoteDocument();
+ }
+
+ public void start() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLED] start()");
+ }
+ if (mState != STATE_INITIAL) {
+ throw new IllegalStateException("Cannot start in state:" + stateToString(mState));
+ }
+ try {
+ mPrintDocumentAdapter.start();
+ mState = STATE_STARTED;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling start()", re);
+ mState = STATE_FAILED;
+ mDocumentObserver.onDestroy();
+ }
+ }
+
+ public boolean update(PrintAttributes attributes, PageRange[] pages, boolean preview) {
+ boolean willUpdate;
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLED] update()");
+ }
+
+ if (hasUpdateError()) {
+ throw new IllegalStateException("Cannot update without a clearing the failure");
+ }
+
+ if (mState == STATE_INITIAL || mState == STATE_FINISHED || mState == STATE_DESTROYED) {
+ throw new IllegalStateException("Cannot update in state:" + stateToString(mState));
+ }
+
+ // We schedule a layout if the constraints changed.
+ if (!mUpdateSpec.hasSameConstraints(attributes, preview)) {
+ willUpdate = true;
+
+ // If there is a current command that is running we ask for a
+ // cancellation and start over.
+ if (mCurrentCommand != null && (mCurrentCommand.isRunning()
+ || mCurrentCommand.isPending())) {
+ mCurrentCommand.cancel();
+ }
+
+ // Schedule a layout command.
+ PrintAttributes oldAttributes = mDocumentInfo.attributes != null
+ ? mDocumentInfo.attributes : new PrintAttributes.Builder().build();
+ AsyncCommand command = new LayoutCommand(mLooper, mPrintDocumentAdapter,
+ mDocumentInfo, oldAttributes, attributes, preview, mCommandResultCallback);
+ scheduleCommand(command);
+
+ mState = STATE_UPDATING;
+ // If no layout in progress and we don't have all pages - schedule a write.
+ } else if ((!(mCurrentCommand instanceof LayoutCommand)
+ || (!mCurrentCommand.isPending() && !mCurrentCommand.isRunning()))
+ && !PageRangeUtils.contains(mUpdateSpec.pages, pages)) {
+ willUpdate = true;
+
+ // Cancel the current write as a new one is to be scheduled.
+ if (mCurrentCommand instanceof WriteCommand
+ && (mCurrentCommand.isPending() || mCurrentCommand.isRunning())) {
+ mCurrentCommand.cancel();
+ }
+
+ // Schedule a write command.
+ AsyncCommand command = new WriteCommand(mContext, mLooper, mPrintDocumentAdapter,
+ mDocumentInfo, mDocumentInfo.info.getPageCount(), pages,
+ mDocumentInfo.file, mCommandResultCallback);
+ scheduleCommand(command);
+
+ mState = STATE_UPDATING;
+ } else {
+ willUpdate = false;
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[SKIPPING] No update needed");
+ }
+ }
+
+ // Keep track of what is requested.
+ mUpdateSpec.update(attributes, preview, pages);
+
+ runPendingCommand();
+
+ return willUpdate;
+ }
+
+ public void finish() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLED] finish()");
+ }
+ if (mState != STATE_STARTED && mState != STATE_UPDATED
+ && mState != STATE_FAILED && mState != STATE_CANCELING) {
+ throw new IllegalStateException("Cannot finish in state:"
+ + stateToString(mState));
+ }
+ try {
+ mPrintDocumentAdapter.finish();
+ mState = STATE_FINISHED;
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling finish()", re);
+ mState = STATE_FAILED;
+ mDocumentObserver.onDestroy();
+ }
+ }
+
+ public void cancel() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLED] cancel()");
+ }
+
+ if (mState == STATE_CANCELING) {
+ return;
+ }
+
+ if (mState != STATE_UPDATING) {
+ throw new IllegalStateException("Cannot cancel in state:" + stateToString(mState));
+ }
+
+ mState = STATE_CANCELING;
+
+ mCurrentCommand.cancel();
+ }
+
+ public void destroy() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLED] destroy()");
+ }
+ if (mState == STATE_DESTROYED) {
+ throw new IllegalStateException("Cannot destroy in state:" + stateToString(mState));
+ }
+
+ mState = STATE_DESTROYED;
+
+ disconnectFromRemoteDocument();
+ mDocumentObserver.onDestroy();
+ }
+
+ public boolean isUpdating() {
+ return mState == STATE_UPDATING || mState == STATE_CANCELING;
+ }
+
+ public boolean isDestroyed() {
+ return mState == STATE_DESTROYED;
+ }
+
+ public boolean hasUpdateError() {
+ return mState == STATE_FAILED;
+ }
+
+ public void clearUpdateError() {
+ if (!hasUpdateError()) {
+ throw new IllegalStateException("No update error to clear");
+ }
+ mState = STATE_STARTED;
+ }
+
+ public RemotePrintDocumentInfo getDocumentInfo() {
+ return mDocumentInfo;
+ }
+
+ public void writeContent(ContentResolver contentResolver, Uri uri) {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ in = new FileInputStream(mDocumentInfo.file);
+ out = contentResolver.openOutputStream(uri);
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ break;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Error writing document content.", e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ }
+ }
+
+ private void notifyUpdateCanceled() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLING] onUpdateCanceled()");
+ }
+ mUpdateCallbacks.onUpdateCanceled();
+ }
+
+ private void notifyUpdateCompleted() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLING] onUpdateCompleted()");
+ }
+ mUpdateCallbacks.onUpdateCompleted(mDocumentInfo);
+ }
+
+ private void notifyUpdateFailed(CharSequence error) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLING] onUpdateCompleted()");
+ }
+ mUpdateCallbacks.onUpdateFailed(error);
+ }
+
+ private void connectToRemoteDocument() {
+ try {
+ mPrintDocumentAdapter.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "The printing process is dead.");
+ destroy();
+ return;
+ }
+
+ try {
+ mPrintDocumentAdapter.setObserver(new PrintDocumentAdapterObserver(this));
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error setting observer to the print adapter.");
+ destroy();
+ }
+ }
+
+ private void disconnectFromRemoteDocument() {
+ try {
+ mPrintDocumentAdapter.setObserver(null);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error setting observer to the print adapter.");
+ // Keep going - best effort...
+ }
+
+ mPrintDocumentAdapter.asBinder().unlinkToDeath(mDeathRecipient, 0);
+ }
+
+ private void scheduleCommand(AsyncCommand command) {
+ if (mCurrentCommand == null) {
+ mCurrentCommand = command;
+ } else {
+ mNextCommand = command;
+ }
+ }
+
+ private void runPendingCommand() {
+ if (mCurrentCommand != null
+ && (mCurrentCommand.isCompleted()
+ || mCurrentCommand.isCanceled())) {
+ mCurrentCommand = mNextCommand;
+ mNextCommand = null;
+ }
+
+ if (mCurrentCommand != null) {
+ if (mCurrentCommand.isPending()) {
+ mCurrentCommand.run();
+ }
+ mState = STATE_UPDATING;
+ } else {
+ mState = STATE_UPDATED;
+ }
+ }
+
+ private static String stateToString(int state) {
+ switch (state) {
+ case STATE_FINISHED: {
+ return "STATE_FINISHED";
+ }
+ case STATE_FAILED: {
+ return "STATE_FAILED";
+ }
+ case STATE_STARTED: {
+ return "STATE_STARTED";
+ }
+ case STATE_UPDATING: {
+ return "STATE_UPDATING";
+ }
+ case STATE_UPDATED: {
+ return "STATE_UPDATED";
+ }
+ case STATE_CANCELING: {
+ return "STATE_CANCELING";
+ }
+ case STATE_CANCELED: {
+ return "STATE_CANCELED";
+ }
+ case STATE_DESTROYED: {
+ return "STATE_DESTROYED";
+ }
+ default: {
+ return "STATE_UNKNOWN";
+ }
+ }
+ }
+
+ private static PageRange[] computePrintedPages(PageRange[] requestedPages,
+ PageRange[] writtenPages, int pageCount) {
+ // Adjust the print job pages based on what was requested and written.
+ // The cases are ordered in the most expected to the least expected.
+ if (Arrays.equals(writtenPages, requestedPages)) {
+ // We got a document with exactly the pages we wanted. Hence,
+ // the printer has to print all pages in the data.
+ return ALL_PAGES_ARRAY;
+ } else if (Arrays.equals(writtenPages, ALL_PAGES_ARRAY)) {
+ // We requested specific pages but got all of them. Hence,
+ // the printer has to print only the requested pages.
+ return requestedPages;
+ } else if (PageRangeUtils.contains(writtenPages, requestedPages)) {
+ // We requested specific pages and got more but not all pages.
+ // Hence, we have to offset appropriately the printed pages to
+ // be based off the start of the written ones instead of zero.
+ // The written pages are always non-null and not empty.
+ final int offset = -writtenPages[0].getStart();
+ PageRangeUtils.offset(requestedPages, offset);
+ return requestedPages;
+ } else if (Arrays.equals(requestedPages, ALL_PAGES_ARRAY)
+ && isAllPages(writtenPages, pageCount)) {
+ // We requested all pages via the special constant and got all
+ // of them as an explicit enumeration. Hence, the printer has
+ // to print only the requested pages.
+ return ALL_PAGES_ARRAY;
+ }
+
+ return null;
+ }
+
+ private static boolean isAllPages(PageRange[] pageRanges, int pageCount) {
+ return pageRanges.length > 0 && pageRanges[0].getStart() == 0
+ && pageRanges[pageRanges.length - 1].getEnd() == pageCount - 1;
+ }
+
+ static final class UpdateSpec {
+ final PrintAttributes attributes = new PrintAttributes.Builder().build();
+ boolean preview;
+ PageRange[] pages;
+
+ public void update(PrintAttributes attributes, boolean preview,
+ PageRange[] pages) {
+ this.attributes.copyFrom(attributes);
+ this.preview = preview;
+ this.pages = Arrays.copyOf(pages, pages.length);
+ }
+
+ public void reset() {
+ attributes.clear();
+ preview = false;
+ pages = null;
+ }
+
+ public boolean hasSameConstraints(PrintAttributes attributes, boolean preview) {
+ return this.attributes.equals(attributes) && this.preview == preview;
+ }
+ }
+
+ public static final class RemotePrintDocumentInfo {
+ public PrintAttributes attributes;
+ public Bundle metadata;
+ public PrintDocumentInfo info;
+ public PageRange[] printedPages;
+ public PageRange[] writtenPages;
+ public File file;
+ }
+
+ private interface CommandDoneCallback {
+ public void onDone();
+ }
+
+ private static abstract class AsyncCommand implements Runnable {
+ private static final int STATE_PENDING = 0;
+ private static final int STATE_RUNNING = 1;
+ private static final int STATE_COMPLETED = 2;
+ private static final int STATE_CANCELED = 3;
+ private static final int STATE_CANCELING = 4;
+ private static final int STATE_FAILED = 5;
+
+ private static int sSequenceCounter;
+
+ protected final int mSequence = sSequenceCounter++;
+ protected final IPrintDocumentAdapter mAdapter;
+ protected final RemotePrintDocumentInfo mDocument;
+
+ protected final CommandDoneCallback mDoneCallback;
+
+ protected ICancellationSignal mCancellation;
+
+ private CharSequence mError;
+
+ private int mState = STATE_PENDING;
+
+ public AsyncCommand(IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
+ CommandDoneCallback doneCallback) {
+ mAdapter = adapter;
+ mDocument = document;
+ mDoneCallback = doneCallback;
+ }
+
+ protected final boolean isCanceling() {
+ return mState == STATE_CANCELING;
+ }
+
+ public final boolean isCanceled() {
+ return mState == STATE_CANCELED;
+ }
+
+ public final void cancel() {
+ if (isRunning()) {
+ canceling();
+ if (mCancellation != null) {
+ try {
+ mCancellation.cancel();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while canceling", re);
+ }
+ }
+ } else {
+ canceled();
+
+ // Done.
+ mDoneCallback.onDone();
+ }
+ }
+
+ protected final void canceling() {
+ if (mState != STATE_PENDING && mState != STATE_RUNNING) {
+ throw new IllegalStateException("Command not pending or running.");
+ }
+ mState = STATE_CANCELING;
+ }
+
+ protected final void canceled() {
+ if (mState != STATE_CANCELING) {
+ throw new IllegalStateException("Not canceling.");
+ }
+ mState = STATE_CANCELED;
+ }
+
+ public final boolean isPending() {
+ return mState == STATE_PENDING;
+ }
+
+ protected final void running() {
+ if (mState != STATE_PENDING) {
+ throw new IllegalStateException("Not pending.");
+ }
+ mState = STATE_RUNNING;
+ }
+
+ public final boolean isRunning() {
+ return mState == STATE_RUNNING;
+ }
+
+ protected final void completed() {
+ if (mState != STATE_RUNNING && mState != STATE_CANCELING) {
+ throw new IllegalStateException("Not running.");
+ }
+ mState = STATE_COMPLETED;
+ }
+
+ public final boolean isCompleted() {
+ return mState == STATE_COMPLETED;
+ }
+
+ protected final void failed(CharSequence error) {
+ if (mState != STATE_RUNNING) {
+ throw new IllegalStateException("Not running.");
+ }
+ mState = STATE_FAILED;
+
+ mError = error;
+ }
+
+ public final boolean isFailed() {
+ return mState == STATE_FAILED;
+ }
+
+ public CharSequence getError() {
+ return mError;
+ }
+ }
+
+ private static final class LayoutCommand extends AsyncCommand {
+ private final PrintAttributes mOldAttributes = new PrintAttributes.Builder().build();
+ private final PrintAttributes mNewAttributes = new PrintAttributes.Builder().build();
+ private final Bundle mMetadata = new Bundle();
+
+ private final ILayoutResultCallback mRemoteResultCallback;
+
+ private final Handler mHandler;
+
+ private boolean mDocumentChanged;
+
+ public LayoutCommand(Looper looper, IPrintDocumentAdapter adapter,
+ RemotePrintDocumentInfo document, PrintAttributes oldAttributes,
+ PrintAttributes newAttributes, boolean preview, CommandDoneCallback callback) {
+ super(adapter, document, callback);
+ mHandler = new LayoutHandler(looper);
+ mRemoteResultCallback = new LayoutResultCallback(mHandler);
+ mOldAttributes.copyFrom(oldAttributes);
+ mNewAttributes.copyFrom(newAttributes);
+ mMetadata.putBoolean(PrintDocumentAdapter.EXTRA_PRINT_PREVIEW, preview);
+ }
+
+ public boolean isDocumentChanged() {
+ return mDocumentChanged;
+ }
+
+ @Override
+ public void run() {
+ running();
+
+ try {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[PERFORMING] layout");
+ }
+ mAdapter.layout(mOldAttributes, mNewAttributes, mRemoteResultCallback,
+ mMetadata, mSequence);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error calling layout", re);
+ handleOnLayoutFailed(null, mSequence);
+ }
+ }
+
+ private void handleOnLayoutStarted(ICancellationSignal cancellation, int sequence) {
+ if (sequence != mSequence) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] onLayoutStarted");
+ }
+
+ if (isCanceling()) {
+ try {
+ cancellation.cancel();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error cancelling", re);
+ handleOnLayoutFailed(null, mSequence);
+ }
+ } else {
+ mCancellation = cancellation;
+ }
+ }
+
+ private void handleOnLayoutFinished(PrintDocumentInfo info,
+ boolean changed, int sequence) {
+ if (sequence != mSequence) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] onLayoutFinished");
+ }
+
+ completed();
+
+ // If the document description changed or the content in the
+ // document changed, the we need to invalidate the pages.
+ if (changed || !equalsIgnoreSize(mDocument.info, info)) {
+ // If the content changed we throw away all pages as
+ // we will request them again with the new content.
+ mDocument.writtenPages = null;
+ mDocument.printedPages = null;
+ mDocumentChanged = true;
+ }
+
+ // Update the document with data from the layout pass.
+ mDocument.attributes = mNewAttributes;
+ mDocument.metadata = mMetadata;
+ mDocument.info = info;
+
+ // Release the remote cancellation interface.
+ mCancellation = null;
+
+ // Done.
+ mDoneCallback.onDone();
+ }
+
+ private void handleOnLayoutFailed(CharSequence error, int sequence) {
+ if (sequence != mSequence) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] onLayoutFailed");
+ }
+
+ failed(error);
+
+ // Release the remote cancellation interface.
+ mCancellation = null;
+
+ // Failed.
+ mDoneCallback.onDone();
+ }
+
+ private void handleOnLayoutCanceled(int sequence) {
+ if (sequence != mSequence) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] onLayoutCanceled");
+ }
+
+ canceled();
+
+ // Release the remote cancellation interface.
+ mCancellation = null;
+
+ // Done.
+ mDoneCallback.onDone();
+ }
+
+ private boolean equalsIgnoreSize(PrintDocumentInfo lhs, PrintDocumentInfo rhs) {
+ if (lhs == rhs) {
+ return true;
+ }
+ if (lhs == null) {
+ return false;
+ } else {
+ if (rhs == null) {
+ return false;
+ }
+ if (lhs.getContentType() != rhs.getContentType()
+ || lhs.getPageCount() != rhs.getPageCount()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private final class LayoutHandler extends Handler {
+ public static final int MSG_ON_LAYOUT_STARTED = 1;
+ public static final int MSG_ON_LAYOUT_FINISHED = 2;
+ public static final int MSG_ON_LAYOUT_FAILED = 3;
+ public static final int MSG_ON_LAYOUT_CANCELED = 4;
+
+ public LayoutHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_ON_LAYOUT_STARTED: {
+ ICancellationSignal cancellation = (ICancellationSignal) message.obj;
+ final int sequence = message.arg1;
+ handleOnLayoutStarted(cancellation, sequence);
+ } break;
+
+ case MSG_ON_LAYOUT_FINISHED: {
+ PrintDocumentInfo info = (PrintDocumentInfo) message.obj;
+ final boolean changed = (message.arg1 == 1);
+ final int sequence = message.arg2;
+ handleOnLayoutFinished(info, changed, sequence);
+ } break;
+
+ case MSG_ON_LAYOUT_FAILED: {
+ CharSequence error = (CharSequence) message.obj;
+ final int sequence = message.arg1;
+ handleOnLayoutFailed(error, sequence);
+ } break;
+
+ case MSG_ON_LAYOUT_CANCELED: {
+ final int sequence = message.arg1;
+ handleOnLayoutCanceled(sequence);
+ } break;
+ }
+ }
+ }
+
+ private static final class LayoutResultCallback extends ILayoutResultCallback.Stub {
+ private final WeakReference<Handler> mWeakHandler;
+
+ public LayoutResultCallback(Handler handler) {
+ mWeakHandler = new WeakReference<>(handler);
+ }
+
+ @Override
+ public void onLayoutStarted(ICancellationSignal cancellation, int sequence) {
+ Handler handler = mWeakHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(LayoutHandler.MSG_ON_LAYOUT_STARTED,
+ sequence, 0, cancellation).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onLayoutFinished(PrintDocumentInfo info, boolean changed, int sequence) {
+ Handler handler = mWeakHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(LayoutHandler.MSG_ON_LAYOUT_FINISHED,
+ changed ? 1 : 0, sequence, info).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onLayoutFailed(CharSequence error, int sequence) {
+ Handler handler = mWeakHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(LayoutHandler.MSG_ON_LAYOUT_FAILED,
+ sequence, 0, error).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onLayoutCanceled(int sequence) {
+ Handler handler = mWeakHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(LayoutHandler.MSG_ON_LAYOUT_CANCELED,
+ sequence, 0).sendToTarget();
+ }
+ }
+ }
+ }
+
+ private static final class WriteCommand extends AsyncCommand {
+ private final int mPageCount;
+ private final PageRange[] mPages;
+ private final File mContentFile;
+
+ private final IWriteResultCallback mRemoteResultCallback;
+ private final CommandDoneCallback mDoneCallback;
+
+ private final Context mContext;
+ private final Handler mHandler;
+
+ public WriteCommand(Context context, Looper looper, IPrintDocumentAdapter adapter,
+ RemotePrintDocumentInfo document, int pageCount, PageRange[] pages,
+ File contentFile, CommandDoneCallback callback) {
+ super(adapter, document, callback);
+ mContext = context;
+ mHandler = new WriteHandler(looper);
+ mRemoteResultCallback = new WriteResultCallback(mHandler);
+ mPageCount = pageCount;
+ mPages = Arrays.copyOf(pages, pages.length);
+ mContentFile = contentFile;
+ mDoneCallback = callback;
+ }
+
+ @Override
+ public void run() {
+ running();
+
+ // This is a long running operation as we will be reading fully
+ // the written data. In case of a cancellation, we ask the client
+ // to stop writing data and close the file descriptor after
+ // which we will reach the end of the stream, thus stop reading.
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ InputStream in = null;
+ OutputStream out = null;
+ ParcelFileDescriptor source = null;
+ ParcelFileDescriptor sink = null;
+ try {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ source = pipe[0];
+ sink = pipe[1];
+
+ in = new FileInputStream(source.getFileDescriptor());
+ out = new FileOutputStream(mContentFile);
+
+ // Async call to initiate the other process writing the data.
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[PERFORMING] write");
+ }
+ mAdapter.write(mPages, sink, mRemoteResultCallback, mSequence);
+
+ // Close the source. It is now held by the client.
+ sink.close();
+ sink = null;
+
+ // Read the data.
+ final byte[] buffer = new byte[8192];
+ while (true) {
+ final int readByteCount = in.read(buffer);
+ if (readByteCount < 0) {
+ break;
+ }
+ out.write(buffer, 0, readByteCount);
+ }
+ } catch (RemoteException | IOException e) {
+ Log.e(LOG_TAG, "Error calling write()", e);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(sink);
+ IoUtils.closeQuietly(source);
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+ }
+
+ private void handleOnWriteStarted(ICancellationSignal cancellation, int sequence) {
+ if (sequence != mSequence) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] onWriteStarted");
+ }
+
+ if (isCanceling()) {
+ try {
+ cancellation.cancel();
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error cancelling", re);
+ handleOnWriteFailed(null, sequence);
+ }
+ } else {
+ mCancellation = cancellation;
+ }
+ }
+
+ private void handleOnWriteFinished(PageRange[] pages, int sequence) {
+ if (sequence != mSequence) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] onWriteFinished");
+ }
+
+ PageRange[] writtenPages = PageRangeUtils.normalize(pages);
+ PageRange[] printedPages = computePrintedPages(mPages, writtenPages, mPageCount);
+
+ // Handle if we got invalid pages
+ if (printedPages != null) {
+ mDocument.writtenPages = writtenPages;
+ mDocument.printedPages = printedPages;
+ completed();
+ } else {
+ mDocument.writtenPages = null;
+ mDocument.printedPages = null;
+ failed(mContext.getString(R.string.print_error_default_message));
+ }
+
+ // Release the remote cancellation interface.
+ mCancellation = null;
+
+ // Done.
+ mDoneCallback.onDone();
+ }
+
+ private void handleOnWriteFailed(CharSequence error, int sequence) {
+ if (sequence != mSequence) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] onWriteFailed");
+ }
+
+ failed(error);
+
+ // Release the remote cancellation interface.
+ mCancellation = null;
+
+ // Done.
+ mDoneCallback.onDone();
+ }
+
+ private void handleOnWriteCanceled(int sequence) {
+ if (sequence != mSequence) {
+ return;
+ }
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[CALLBACK] onWriteCanceled");
+ }
+
+ canceled();
+
+ // Release the remote cancellation interface.
+ mCancellation = null;
+
+ // Done.
+ mDoneCallback.onDone();
+ }
+
+ private final class WriteHandler extends Handler {
+ public static final int MSG_ON_WRITE_STARTED = 1;
+ public static final int MSG_ON_WRITE_FINISHED = 2;
+ public static final int MSG_ON_WRITE_FAILED = 3;
+ public static final int MSG_ON_WRITE_CANCELED = 4;
+
+ public WriteHandler(Looper looper) {
+ super(looper, null, false);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_ON_WRITE_STARTED: {
+ ICancellationSignal cancellation = (ICancellationSignal) message.obj;
+ final int sequence = message.arg1;
+ handleOnWriteStarted(cancellation, sequence);
+ } break;
+
+ case MSG_ON_WRITE_FINISHED: {
+ PageRange[] pages = (PageRange[]) message.obj;
+ final int sequence = message.arg1;
+ handleOnWriteFinished(pages, sequence);
+ } break;
+
+ case MSG_ON_WRITE_FAILED: {
+ CharSequence error = (CharSequence) message.obj;
+ final int sequence = message.arg1;
+ handleOnWriteFailed(error, sequence);
+ } break;
+
+ case MSG_ON_WRITE_CANCELED: {
+ final int sequence = message.arg1;
+ handleOnWriteCanceled(sequence);
+ } break;
+ }
+ }
+ }
+
+ private static final class WriteResultCallback extends IWriteResultCallback.Stub {
+ private final WeakReference<Handler> mWeakHandler;
+
+ public WriteResultCallback(Handler handler) {
+ mWeakHandler = new WeakReference<>(handler);
+ }
+
+ @Override
+ public void onWriteStarted(ICancellationSignal cancellation, int sequence) {
+ Handler handler = mWeakHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(WriteHandler.MSG_ON_WRITE_STARTED,
+ sequence, 0, cancellation).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onWriteFinished(PageRange[] pages, int sequence) {
+ Handler handler = mWeakHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(WriteHandler.MSG_ON_WRITE_FINISHED,
+ sequence, 0, pages).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onWriteFailed(CharSequence error, int sequence) {
+ Handler handler = mWeakHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(WriteHandler.MSG_ON_WRITE_FAILED,
+ sequence, 0, error).sendToTarget();
+ }
+ }
+
+ @Override
+ public void onWriteCanceled(int sequence) {
+ Handler handler = mWeakHandler.get();
+ if (handler != null) {
+ handler.obtainMessage(WriteHandler.MSG_ON_WRITE_CANCELED,
+ sequence, 0).sendToTarget();
+ }
+ }
+ }
+ }
+
+ private static final class PrintDocumentAdapterObserver
+ extends IPrintDocumentAdapterObserver.Stub {
+ private final WeakReference<RemotePrintDocument> mWeakDocument;
+
+ public PrintDocumentAdapterObserver(RemotePrintDocument document) {
+ mWeakDocument = new WeakReference<>(document);
+ }
+
+ @Override
+ public void onDestroy() {
+ final RemotePrintDocument document = mWeakDocument.get();
+ if (document != null) {
+ new Handler(document.mLooper).post(new Runnable() {
+ @Override
+ public void run() {
+ document.mDocumentObserver.onDestroy();
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
new file mode 100644
index 0000000..d802cd8
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -0,0 +1,710 @@
+/*
+ * Copyright (C) 2013 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.printspooler.ui;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Loader;
+import android.content.pm.ServiceInfo;
+import android.os.AsyncTask;
+import android.print.PrintManager;
+import android.print.PrinterDiscoverySession;
+import android.print.PrinterDiscoverySession.OnPrintersChangeListener;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.PrintServiceInfo;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import libcore.io.IoUtils;
+
+/**
+ * This class is responsible for loading printers by doing discovery
+ * and merging the discovered printers with the previously used ones.
+ */
+public final class FusedPrintersProvider extends Loader<List<PrinterInfo>> {
+ private static final String LOG_TAG = "FusedPrintersProvider";
+
+ private static final boolean DEBUG = false;
+
+ private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f;
+ private static final int MAX_HISTORY_LENGTH = 50;
+
+ private static final int MAX_FAVORITE_PRINTER_COUNT = 4;
+
+ private final List<PrinterInfo> mPrinters =
+ new ArrayList<PrinterInfo>();
+
+ private final List<PrinterInfo> mFavoritePrinters =
+ new ArrayList<PrinterInfo>();
+
+ private final PersistenceManager mPersistenceManager;
+
+ private PrinterDiscoverySession mDiscoverySession;
+
+ private PrinterId mTrackedPrinter;
+
+ private boolean mPrintersUpdatedBefore;
+
+ public FusedPrintersProvider(Context context) {
+ super(context);
+ mPersistenceManager = new PersistenceManager(context);
+ }
+
+ public void addHistoricalPrinter(PrinterInfo printer) {
+ mPersistenceManager.addPrinterAndWritePrinterHistory(printer);
+ }
+
+ private void computeAndDeliverResult(ArrayMap<PrinterId, PrinterInfo> discoveredPrinters,
+ ArrayMap<PrinterId, PrinterInfo> favoritePrinters) {
+ List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+ // Add the updated favorite printers.
+ final int favoritePrinterCount = favoritePrinters.size();
+ for (int i = 0; i < favoritePrinterCount; i++) {
+ PrinterInfo favoritePrinter = favoritePrinters.valueAt(i);
+ PrinterInfo updatedPrinter = discoveredPrinters.remove(
+ favoritePrinter.getId());
+ if (updatedPrinter != null) {
+ printers.add(updatedPrinter);
+ } else {
+ printers.add(favoritePrinter);
+ }
+ }
+
+ // Add other updated printers.
+ final int printerCount = mPrinters.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo printer = mPrinters.get(i);
+ PrinterInfo updatedPrinter = discoveredPrinters.remove(
+ printer.getId());
+ if (updatedPrinter != null) {
+ printers.add(updatedPrinter);
+ }
+ }
+
+ // Add the new printers, i.e. what is left.
+ printers.addAll(discoveredPrinters.values());
+
+ // Update the list of printers.
+ mPrinters.clear();
+ mPrinters.addAll(printers);
+
+ if (isStarted()) {
+ // If stated deliver the new printers.
+ deliverResult(printers);
+ } else {
+ // Otherwise, take a note for the change.
+ onContentChanged();
+ }
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onStartLoading() " + FusedPrintersProvider.this.hashCode());
+ }
+ // The contract is that if we already have a valid,
+ // result the we have to deliver it immediately.
+ if (!mPrinters.isEmpty()) {
+ deliverResult(new ArrayList<PrinterInfo>(mPrinters));
+ }
+ // Always load the data to ensure discovery period is
+ // started and to make sure obsolete printers are updated.
+ onForceLoad();
+ }
+
+ @Override
+ protected void onStopLoading() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onStopLoading() " + FusedPrintersProvider.this.hashCode());
+ }
+ onCancelLoad();
+ }
+
+ @Override
+ protected void onForceLoad() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onForceLoad() " + FusedPrintersProvider.this.hashCode());
+ }
+ loadInternal();
+ }
+
+ private void loadInternal() {
+ if (mDiscoverySession == null) {
+ PrintManager printManager = (PrintManager) getContext()
+ .getSystemService(Context.PRINT_SERVICE);
+ mDiscoverySession = printManager.createPrinterDiscoverySession();
+ mPersistenceManager.readPrinterHistory();
+ } else if (mPersistenceManager.isHistoryChanged()) {
+ mPersistenceManager.readPrinterHistory();
+ }
+ if (mPersistenceManager.isReadHistoryCompleted()
+ && !mDiscoverySession.isPrinterDiscoveryStarted()) {
+ mDiscoverySession.setOnPrintersChangeListener(new OnPrintersChangeListener() {
+ @Override
+ public void onPrintersChanged() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onPrintersChanged() count:"
+ + mDiscoverySession.getPrinters().size()
+ + " " + FusedPrintersProvider.this.hashCode());
+ }
+ updatePrinters(mDiscoverySession.getPrinters(), mFavoritePrinters);
+ }
+ });
+ final int favoriteCount = mFavoritePrinters.size();
+ List<PrinterId> printerIds = new ArrayList<PrinterId>(favoriteCount);
+ for (int i = 0; i < favoriteCount; i++) {
+ printerIds.add(mFavoritePrinters.get(i).getId());
+ }
+ mDiscoverySession.startPrinterDiscovery(printerIds);
+ List<PrinterInfo> printers = mDiscoverySession.getPrinters();
+ if (!printers.isEmpty()) {
+ updatePrinters(printers, mFavoritePrinters);
+ }
+ }
+ }
+
+ private void updatePrinters(List<PrinterInfo> printers, List<PrinterInfo> favoritePrinters) {
+ if (mPrintersUpdatedBefore && mPrinters.equals(printers)
+ && mFavoritePrinters.equals(favoritePrinters)) {
+ return;
+ }
+
+ mPrintersUpdatedBefore = true;
+
+ ArrayMap<PrinterId, PrinterInfo> printersMap =
+ new ArrayMap<PrinterId, PrinterInfo>();
+ final int printerCount = printers.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo printer = printers.get(i);
+ printersMap.put(printer.getId(), printer);
+ }
+
+ ArrayMap<PrinterId, PrinterInfo> favoritePrintersMap =
+ new ArrayMap<PrinterId, PrinterInfo>();
+ final int favoritePrinterCount = favoritePrinters.size();
+ for (int i = 0; i < favoritePrinterCount; i++) {
+ PrinterInfo favoritePrinter = favoritePrinters.get(i);
+ favoritePrintersMap.put(favoritePrinter.getId(), favoritePrinter);
+ }
+
+ computeAndDeliverResult(printersMap, favoritePrintersMap);
+ }
+
+ @Override
+ protected boolean onCancelLoad() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onCancelLoad() " + FusedPrintersProvider.this.hashCode());
+ }
+ return cancelInternal();
+ }
+
+ private boolean cancelInternal() {
+ if (mDiscoverySession != null
+ && mDiscoverySession.isPrinterDiscoveryStarted()) {
+ if (mTrackedPrinter != null) {
+ mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
+ mTrackedPrinter = null;
+ }
+ mDiscoverySession.stopPrinterDiscovery();
+ return true;
+ } else if (mPersistenceManager.isReadHistoryInProgress()) {
+ return mPersistenceManager.stopReadPrinterHistory();
+ }
+ return false;
+ }
+
+ @Override
+ protected void onReset() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onReset() " + FusedPrintersProvider.this.hashCode());
+ }
+ onStopLoading();
+ mPrinters.clear();
+ if (mDiscoverySession != null) {
+ mDiscoverySession.destroy();
+ mDiscoverySession = null;
+ }
+ }
+
+ @Override
+ protected void onAbandon() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "onAbandon() " + FusedPrintersProvider.this.hashCode());
+ }
+ onStopLoading();
+ }
+
+ public void setTrackedPrinter(PrinterId printerId) {
+ if (isStarted() && mDiscoverySession != null
+ && mDiscoverySession.isPrinterDiscoveryStarted()) {
+ if (mTrackedPrinter != null) {
+ if (mTrackedPrinter.equals(printerId)) {
+ return;
+ }
+ mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter);
+ }
+ mTrackedPrinter = printerId;
+ if (printerId != null) {
+ mDiscoverySession.startPrinterStateTracking(printerId);
+ }
+ }
+ }
+
+ public boolean isFavoritePrinter(PrinterId printerId) {
+ final int printerCount = mFavoritePrinters.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo favoritePritner = mFavoritePrinters.get(i);
+ if (favoritePritner.getId().equals(printerId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void forgetFavoritePrinter(PrinterId printerId) {
+ List<PrinterInfo> newFavoritePrinters = null;
+
+ // Remove the printer from the favorites.
+ final int favoritePrinterCount = mFavoritePrinters.size();
+ for (int i = 0; i < favoritePrinterCount; i++) {
+ PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
+ if (favoritePrinter.getId().equals(printerId)) {
+ newFavoritePrinters = new ArrayList<PrinterInfo>();
+ newFavoritePrinters.addAll(mPrinters);
+ newFavoritePrinters.remove(i);
+ break;
+ }
+ }
+
+ // If we removed a favorite printer, we have work to do.
+ if (newFavoritePrinters != null) {
+ // Remove the printer from history and persist the latter.
+ mPersistenceManager.removeHistoricalPrinterAndWritePrinterHistory(printerId);
+
+ // Recompute and deliver the printers.
+ updatePrinters(mDiscoverySession.getPrinters(), newFavoritePrinters);
+ }
+ }
+
+ private final class PersistenceManager {
+ private static final String PERSIST_FILE_NAME = "printer_history.xml";
+
+ private static final String TAG_PRINTERS = "printers";
+
+ private static final String TAG_PRINTER = "printer";
+ private static final String TAG_PRINTER_ID = "printerId";
+
+ private static final String ATTR_LOCAL_ID = "localId";
+ private static final String ATTR_SERVICE_NAME = "serviceName";
+
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_DESCRIPTION = "description";
+ private static final String ATTR_STATUS = "status";
+
+ private final AtomicFile mStatePersistFile;
+
+ private List<PrinterInfo> mHistoricalPrinters = new ArrayList<PrinterInfo>();
+
+ private boolean mReadHistoryCompleted;
+ private boolean mReadHistoryInProgress;
+
+ private ReadTask mReadTask;
+
+ private volatile long mLastReadHistoryTimestamp;
+
+ private PersistenceManager(Context context) {
+ mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
+ PERSIST_FILE_NAME));
+ }
+
+ public boolean isReadHistoryInProgress() {
+ return mReadHistoryInProgress;
+ }
+
+ public boolean isReadHistoryCompleted() {
+ return mReadHistoryCompleted;
+ }
+
+ public boolean stopReadPrinterHistory() {
+ final boolean cancelled = mReadTask.cancel(true);
+ mReadTask = null;
+ return cancelled;
+ }
+
+ public void readPrinterHistory() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "read history started "
+ + FusedPrintersProvider.this.hashCode());
+ }
+ mReadHistoryInProgress = true;
+ mReadTask = new ReadTask();
+ mReadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void addPrinterAndWritePrinterHistory(PrinterInfo printer) {
+ if (mHistoricalPrinters.size() >= MAX_HISTORY_LENGTH) {
+ mHistoricalPrinters.remove(0);
+ }
+ mHistoricalPrinters.add(printer);
+ new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
+ new ArrayList<PrinterInfo>(mHistoricalPrinters));
+ }
+
+ @SuppressWarnings("unchecked")
+ public void removeHistoricalPrinterAndWritePrinterHistory(PrinterId printerId) {
+ boolean writeHistory = false;
+ final int printerCount = mHistoricalPrinters.size();
+ for (int i = printerCount - 1; i >= 0; i--) {
+ PrinterInfo historicalPrinter = mHistoricalPrinters.get(i);
+ if (historicalPrinter.getId().equals(printerId)) {
+ mHistoricalPrinters.remove(i);
+ writeHistory = true;
+ }
+ }
+ if (writeHistory) {
+ new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
+ new ArrayList<PrinterInfo>(mHistoricalPrinters));
+ }
+ }
+
+ public boolean isHistoryChanged() {
+ return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
+ }
+
+ private List<PrinterInfo> computeFavoritePrinters(List<PrinterInfo> printers) {
+ Map<PrinterId, PrinterRecord> recordMap =
+ new ArrayMap<PrinterId, PrinterRecord>();
+
+ // Recompute the weights.
+ float currentWeight = 1.0f;
+ final int printerCount = printers.size();
+ for (int i = printerCount - 1; i >= 0; i--) {
+ PrinterInfo printer = printers.get(i);
+ // Aggregate weight for the same printer
+ PrinterRecord record = recordMap.get(printer.getId());
+ if (record == null) {
+ record = new PrinterRecord(printer);
+ recordMap.put(printer.getId(), record);
+ }
+ record.weight += currentWeight;
+ currentWeight *= WEIGHT_DECAY_COEFFICIENT;
+ }
+
+ // Soft the favorite printers.
+ List<PrinterRecord> favoriteRecords = new ArrayList<PrinterRecord>(
+ recordMap.values());
+ Collections.sort(favoriteRecords);
+
+ // Write the favorites to the output.
+ final int favoriteCount = Math.min(favoriteRecords.size(),
+ MAX_FAVORITE_PRINTER_COUNT);
+ List<PrinterInfo> favoritePrinters = new ArrayList<PrinterInfo>(favoriteCount);
+ for (int i = 0; i < favoriteCount; i++) {
+ PrinterInfo printer = favoriteRecords.get(i).printer;
+ favoritePrinters.add(printer);
+ }
+
+ return favoritePrinters;
+ }
+
+ private final class PrinterRecord implements Comparable<PrinterRecord> {
+ public final PrinterInfo printer;
+ public float weight;
+
+ public PrinterRecord(PrinterInfo printer) {
+ this.printer = printer;
+ }
+
+ @Override
+ public int compareTo(PrinterRecord another) {
+ return Float.floatToIntBits(another.weight) - Float.floatToIntBits(weight);
+ }
+ }
+
+ private final class ReadTask extends AsyncTask<Void, Void, List<PrinterInfo>> {
+ @Override
+ protected List<PrinterInfo> doInBackground(Void... args) {
+ return doReadPrinterHistory();
+ }
+
+ @Override
+ protected void onPostExecute(List<PrinterInfo> printers) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "read history completed "
+ + FusedPrintersProvider.this.hashCode());
+ }
+
+ // Ignore printer records whose target services are not enabled.
+ PrintManager printManager = (PrintManager) getContext()
+ .getSystemService(Context.PRINT_SERVICE);
+ List<PrintServiceInfo> services = printManager
+ .getEnabledPrintServices();
+
+ Set<ComponentName> enabledComponents = new ArraySet<ComponentName>();
+ final int installedServiceCount = services.size();
+ for (int i = 0; i < installedServiceCount; i++) {
+ ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
+ ComponentName componentName = new ComponentName(
+ serviceInfo.packageName, serviceInfo.name);
+ enabledComponents.add(componentName);
+ }
+
+ final int printerCount = printers.size();
+ for (int i = printerCount - 1; i >= 0; i--) {
+ ComponentName printerServiceName = printers.get(i).getId().getServiceName();
+ if (!enabledComponents.contains(printerServiceName)) {
+ printers.remove(i);
+ }
+ }
+
+ // Store the filtered list.
+ mHistoricalPrinters = printers;
+
+ // Compute the favorite printers.
+ mFavoritePrinters.clear();
+ mFavoritePrinters.addAll(computeFavoritePrinters(mHistoricalPrinters));
+
+ mReadHistoryInProgress = false;
+ mReadHistoryCompleted = true;
+
+ // Deliver the printers.
+ updatePrinters(mDiscoverySession.getPrinters(), mHistoricalPrinters);
+
+ // Loading the available printers if needed.
+ loadInternal();
+
+ // We are done.
+ mReadTask = null;
+ }
+
+ private List<PrinterInfo> doReadPrinterHistory() {
+ final FileInputStream in;
+ try {
+ in = mStatePersistFile.openRead();
+ } catch (FileNotFoundException fnfe) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "No existing printer history "
+ + FusedPrintersProvider.this.hashCode());
+ }
+ return new ArrayList<PrinterInfo>();
+ }
+ try {
+ List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, null);
+ parseState(parser, printers);
+ // Take a note which version of the history was read.
+ mLastReadHistoryTimestamp = mStatePersistFile.getBaseFile().lastModified();
+ return printers;
+ } catch (IllegalStateException ise) {
+ Slog.w(LOG_TAG, "Failed parsing ", ise);
+ } catch (NullPointerException npe) {
+ Slog.w(LOG_TAG, "Failed parsing ", npe);
+ } catch (NumberFormatException nfe) {
+ Slog.w(LOG_TAG, "Failed parsing ", nfe);
+ } catch (XmlPullParserException xppe) {
+ Slog.w(LOG_TAG, "Failed parsing ", xppe);
+ } catch (IOException ioe) {
+ Slog.w(LOG_TAG, "Failed parsing ", ioe);
+ } catch (IndexOutOfBoundsException iobe) {
+ Slog.w(LOG_TAG, "Failed parsing ", iobe);
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+
+ return Collections.emptyList();
+ }
+
+ private void parseState(XmlPullParser parser, List<PrinterInfo> outPrinters)
+ throws IOException, XmlPullParserException {
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_PRINTERS);
+ parser.next();
+
+ while (parsePrinter(parser, outPrinters)) {
+ // Be nice and respond to cancellation
+ if (isCancelled()) {
+ return;
+ }
+ parser.next();
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PRINTERS);
+ }
+
+ private boolean parsePrinter(XmlPullParser parser, List<PrinterInfo> outPrinters)
+ throws IOException, XmlPullParserException {
+ skipEmptyTextTags(parser);
+ if (!accept(parser, XmlPullParser.START_TAG, TAG_PRINTER)) {
+ return false;
+ }
+
+ String name = parser.getAttributeValue(null, ATTR_NAME);
+ String description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
+ final int status = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATUS));
+
+ parser.next();
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID);
+ String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
+ ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
+ null, ATTR_SERVICE_NAME));
+ PrinterId printerId = new PrinterId(service, localId);
+ parser.next();
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
+ parser.next();
+
+ PrinterInfo.Builder builder = new PrinterInfo.Builder(printerId, name, status);
+ builder.setDescription(description);
+ PrinterInfo printer = builder.build();
+
+ outPrinters.add(printer);
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[RESTORED] " + printer);
+ }
+
+ skipEmptyTextTags(parser);
+ expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
+
+ return true;
+ }
+
+ private void expect(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (!accept(parser, type, tag)) {
+ throw new XmlPullParserException("Exepected event: " + type
+ + " and tag: " + tag + " but got event: " + parser.getEventType()
+ + " and tag:" + parser.getName());
+ }
+ }
+
+ private void skipEmptyTextTags(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ while (accept(parser, XmlPullParser.TEXT, null)
+ && "\n".equals(parser.getText())) {
+ parser.next();
+ }
+ }
+
+ private boolean accept(XmlPullParser parser, int type, String tag)
+ throws IOException, XmlPullParserException {
+ if (parser.getEventType() != type) {
+ return false;
+ }
+ if (tag != null) {
+ if (!tag.equals(parser.getName())) {
+ return false;
+ }
+ } else if (parser.getName() != null) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private final class WriteTask extends AsyncTask<List<PrinterInfo>, Void, Void> {
+ @Override
+ protected Void doInBackground(List<PrinterInfo>... printers) {
+ doWritePrinterHistory(printers[0]);
+ return null;
+ }
+
+ private void doWritePrinterHistory(List<PrinterInfo> printers) {
+ FileOutputStream out = null;
+ try {
+ out = mStatePersistFile.startWrite();
+
+ XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(out, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_PRINTERS);
+
+ final int printerCount = printers.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo printer = printers.get(i);
+
+ serializer.startTag(null, TAG_PRINTER);
+
+ serializer.attribute(null, ATTR_NAME, printer.getName());
+ // Historical printers are always stored as unavailable.
+ serializer.attribute(null, ATTR_STATUS, String.valueOf(
+ PrinterInfo.STATUS_UNAVAILABLE));
+ String description = printer.getDescription();
+ if (description != null) {
+ serializer.attribute(null, ATTR_DESCRIPTION, description);
+ }
+
+ PrinterId printerId = printer.getId();
+ serializer.startTag(null, TAG_PRINTER_ID);
+ serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
+ serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
+ .flattenToString());
+ serializer.endTag(null, TAG_PRINTER_ID);
+
+ serializer.endTag(null, TAG_PRINTER);
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[PERSISTED] " + printer);
+ }
+ }
+
+ serializer.endTag(null, TAG_PRINTERS);
+ serializer.endDocument();
+ mStatePersistFile.finishWrite(out);
+
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[PERSIST END]");
+ }
+ } catch (IOException ioe) {
+ Slog.w(LOG_TAG, "Failed to write printer history, restoring backup.", ioe);
+ mStatePersistFile.failWrite(out);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
new file mode 100644
index 0000000..3e0d7e5
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -0,0 +1,1955 @@
+/*
+ * 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.printspooler.ui;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.print.IPrintDocumentAdapter;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentInfo;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.PrintService;
+import android.provider.DocumentsContract;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.text.TextWatcher;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.android.printspooler.R;
+import com.android.printspooler.model.PrintSpoolerProvider;
+import com.android.printspooler.model.PrintSpoolerService;
+import com.android.printspooler.model.RemotePrintDocument;
+import com.android.printspooler.util.MediaSizeUtils;
+import com.android.printspooler.util.MediaSizeUtils.MediaSizeComparator;
+import com.android.printspooler.util.PageRangeUtils;
+import com.android.printspooler.util.PrintOptionUtils;
+import com.android.printspooler.widget.ContentView;
+import com.android.printspooler.widget.ContentView.OptionsStateChangeListener;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class PrintActivity extends Activity implements RemotePrintDocument.UpdateResultCallbacks,
+ PrintErrorFragment.OnActionListener, PrintProgressFragment.OnCancelRequestListener {
+ private static final String LOG_TAG = "PrintActivity";
+
+ public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
+
+ private static final int ORIENTATION_PORTRAIT = 0;
+ private static final int ORIENTATION_LANDSCAPE = 1;
+
+ private static final int ACTIVITY_REQUEST_CREATE_FILE = 1;
+ private static final int ACTIVITY_REQUEST_SELECT_PRINTER = 2;
+ private static final int ACTIVITY_REQUEST_POPULATE_ADVANCED_PRINT_OPTIONS = 3;
+
+ private static final int DEST_ADAPTER_MAX_ITEM_COUNT = 9;
+
+ private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
+ private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;
+
+ private static final int STATE_CONFIGURING = 0;
+ private static final int STATE_PRINT_CONFIRMED = 1;
+ private static final int STATE_PRINT_CANCELED = 2;
+ private static final int STATE_UPDATE_FAILED = 3;
+ private static final int STATE_CREATE_FILE_FAILED = 4;
+ private static final int STATE_PRINTER_UNAVAILABLE = 5;
+
+ private static final int UI_STATE_PREVIEW = 0;
+ private static final int UI_STATE_ERROR = 1;
+ private static final int UI_STATE_PROGRESS = 2;
+
+ private static final int MIN_COPIES = 1;
+ private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES);
+
+ private static final Pattern PATTERN_DIGITS = Pattern.compile("[\\d]+");
+
+ private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
+ "(?=[]\\[+&|!(){}^\"~*?:\\\\])");
+
+ private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
+ "[\\s]*[0-9]*[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*?(([,])"
+ + "[\\s]*[0-9]*[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*|[\\s]*)+");
+
+ public static final PageRange[] ALL_PAGES_ARRAY = new PageRange[] {PageRange.ALL_PAGES};
+
+ private final PrinterAvailabilityDetector mPrinterAvailabilityDetector =
+ new PrinterAvailabilityDetector();
+
+ private final SimpleStringSplitter mStringCommaSplitter = new SimpleStringSplitter(',');
+
+ private final OnFocusChangeListener mSelectAllOnFocusListener = new SelectAllOnFocusListener();
+
+ private PrintSpoolerProvider mSpoolerProvider;
+
+ private PrintJobInfo mPrintJob;
+ private RemotePrintDocument mPrintedDocument;
+ private PrinterRegistry mPrinterRegistry;
+
+ private EditText mCopiesEditText;
+
+ private TextView mPageRangeOptionsTitle;
+ private TextView mPageRangeTitle;
+ private EditText mPageRangeEditText;
+
+ private Spinner mDestinationSpinner;
+ private DestinationAdapter mDestinationSpinnerAdapter;
+
+ private Spinner mMediaSizeSpinner;
+ private ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
+
+ private Spinner mColorModeSpinner;
+ private ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
+
+ private Spinner mOrientationSpinner;
+ private ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
+
+ private Spinner mRangeOptionsSpinner;
+
+ private ContentView mOptionsContent;
+
+ private TextView mSummaryCopies;
+ private TextView mSummaryPaperSize;
+
+ private View mAdvancedPrintOptionsContainer;
+
+ private Button mMoreOptionsButton;
+
+ private ImageView mPrintButton;
+
+ private ProgressMessageController mProgressMessageController;
+
+ private MediaSizeComparator mMediaSizeComparator;
+
+ private PageRange[] mRequestedPages;
+
+ private PrinterInfo mOldCurrentPrinter;
+
+ private String mCallingPackageName;
+
+ private int mState = STATE_CONFIGURING;
+
+ private int mUiState = UI_STATE_PREVIEW;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle extras = getIntent().getExtras();
+
+ mPrintJob = extras.getParcelable(PrintManager.EXTRA_PRINT_JOB);
+ if (mPrintJob == null) {
+ throw new IllegalArgumentException(PrintManager.EXTRA_PRINT_JOB
+ + " cannot be null");
+ }
+ mPrintJob.setAttributes(new PrintAttributes.Builder().build());
+
+ final IBinder adapter = extras.getBinder(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER);
+ if (adapter == null) {
+ throw new IllegalArgumentException(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER
+ + " cannot be null");
+ }
+
+ mCallingPackageName = extras.getString(DocumentsContract.EXTRA_PACKAGE_NAME);
+
+ // This will take just a few milliseconds, so just wait to
+ // bind to the local service before showing the UI.
+ mSpoolerProvider = new PrintSpoolerProvider(this,
+ new Runnable() {
+ @Override
+ public void run() {
+ // Now that we are bound to the print spooler service,
+ // create the printer registry and wait for it to get
+ // the first batch of results which will be delivered
+ // after reading historical data. This should be pretty
+ // fast, so just wait before showing the UI.
+ mPrinterRegistry = new PrinterRegistry(PrintActivity.this,
+ new Runnable() {
+ @Override
+ public void run() {
+ setTitle(R.string.print_dialog);
+ setContentView(R.layout.print_activity);
+
+ mPrintedDocument = new RemotePrintDocument(PrintActivity.this,
+ IPrintDocumentAdapter.Stub.asInterface(adapter),
+ PrintSpoolerService.generateFileForPrintJob(PrintActivity.this,
+ mPrintJob.getId()),
+ new RemotePrintDocument.DocumentObserver() {
+ @Override
+ public void onDestroy() {
+ finish();
+ }
+ }, PrintActivity.this);
+
+ mProgressMessageController = new ProgressMessageController(PrintActivity.this);
+
+ mMediaSizeComparator = new MediaSizeComparator(PrintActivity.this);
+
+ mDestinationSpinnerAdapter = new DestinationAdapter();
+
+ bindUi();
+
+ updateOptionsUi();
+
+ // Now show the updated UI to avoid flicker.
+ mOptionsContent.setVisibility(View.VISIBLE);
+
+ mRequestedPages = computeRequestedPages();
+
+ mPrintedDocument.start();
+
+ ensurePreviewUiShown();
+ }
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onPause() {
+ if (isFinishing()) {
+ PrintSpoolerService spooler = mSpoolerProvider.getSpooler();
+ if (mState == STATE_PRINT_CONFIRMED) {
+ spooler.updatePrintJobUserConfigurableOptionsNoPersistence(mPrintJob);
+ spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_QUEUED, null);
+ } else {
+ spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_CANCELED, null);
+ }
+ mProgressMessageController.cancel();
+ mPrinterRegistry.setTrackedPrinter(null);
+ mSpoolerProvider.destroy();
+ mPrintedDocument.finish();
+ mPrintedDocument.destroy();
+ }
+
+ mPrinterAvailabilityDetector.cancel();
+
+ super.onPause();
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ event.startTracking();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK
+ && event.isTracking() && !event.isCanceled()) {
+ cancelPrint();
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public void onActionPerformed() {
+ switch (mState) {
+ case STATE_UPDATE_FAILED: {
+ if (canUpdateDocument()) {
+ updateDocument(true, true);
+ ensurePreviewUiShown();
+ mState = STATE_CONFIGURING;
+ updateOptionsUi();
+ }
+ } break;
+
+ case STATE_CREATE_FILE_FAILED: {
+ mState = STATE_CONFIGURING;
+ ensurePreviewUiShown();
+ updateOptionsUi();
+ } break;
+ }
+ }
+
+ @Override
+ public void onCancelRequest() {
+ if (mPrintedDocument.isUpdating()) {
+ mPrintedDocument.cancel();
+ }
+ }
+
+ public void onUpdateCanceled() {
+ mProgressMessageController.cancel();
+ ensurePreviewUiShown();
+ finishIfConfirmedOrCanceled();
+ updateOptionsUi();
+ }
+
+ @Override
+ public void onUpdateCompleted(RemotePrintDocument.RemotePrintDocumentInfo document) {
+ mProgressMessageController.cancel();
+ ensurePreviewUiShown();
+
+ // Update the print job with the info for the written document. The page
+ // count we get from the remote document is the pages in the document from
+ // the app perspective but the print job should contain the page count from
+ // print service perspective which is the pages in the written PDF not the
+ // pages in the printed document.
+ PrintDocumentInfo info = document.info;
+ if (info == null) {
+ return;
+ }
+ final int pageCount = PageRangeUtils.getNormalizedPageCount(document.writtenPages,
+ info.getPageCount());
+ PrintDocumentInfo adjustedInfo = new PrintDocumentInfo.Builder(info.getName())
+ .setContentType(info.getContentType())
+ .setPageCount(pageCount)
+ .build();
+ mPrintJob.setDocumentInfo(adjustedInfo);
+ mPrintJob.setPages(document.printedPages);
+ finishIfConfirmedOrCanceled();
+ updateOptionsUi();
+ }
+
+ @Override
+ public void onUpdateFailed(CharSequence error) {
+ mState = STATE_UPDATE_FAILED;
+ ensureErrorUiShown(error, PrintErrorFragment.ACTION_RETRY);
+ updateOptionsUi();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case ACTIVITY_REQUEST_CREATE_FILE: {
+ onStartCreateDocumentActivityResult(resultCode, data);
+ } break;
+
+ case ACTIVITY_REQUEST_SELECT_PRINTER: {
+ onSelectPrinterActivityResult(resultCode, data);
+ } break;
+
+ case ACTIVITY_REQUEST_POPULATE_ADVANCED_PRINT_OPTIONS: {
+ onAdvancedPrintOptionsActivityResult(resultCode, data);
+ } break;
+ }
+ }
+
+ private void startCreateDocumentActivity() {
+ PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
+ if (info == null) {
+ return;
+ }
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ intent.setType("application/pdf");
+ intent.putExtra(Intent.EXTRA_TITLE, info.getName());
+ intent.putExtra(DocumentsContract.EXTRA_PACKAGE_NAME, mCallingPackageName);
+ startActivityForResult(intent, ACTIVITY_REQUEST_CREATE_FILE);
+ }
+
+ private void onStartCreateDocumentActivityResult(int resultCode, Intent data) {
+ if (resultCode == RESULT_OK && data != null) {
+ Uri uri = data.getData();
+ mPrintedDocument.writeContent(getContentResolver(), uri);
+ finish();
+ } else if (resultCode == RESULT_CANCELED) {
+ mState = STATE_CONFIGURING;
+ updateOptionsUi();
+ } else {
+ ensureErrorUiShown(getString(R.string.print_write_error_message),
+ PrintErrorFragment.ACTION_CONFIRM);
+ mState = STATE_CREATE_FILE_FAILED;
+ updateOptionsUi();
+ }
+ }
+
+ private void startSelectPrinterActivity() {
+ Intent intent = new Intent(this, SelectPrinterActivity.class);
+ startActivityForResult(intent, ACTIVITY_REQUEST_SELECT_PRINTER);
+ }
+
+ private void onSelectPrinterActivityResult(int resultCode, Intent data) {
+ if (resultCode == RESULT_OK && data != null) {
+ PrinterId printerId = data.getParcelableExtra(INTENT_EXTRA_PRINTER_ID);
+ if (printerId != null) {
+ mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerId);
+ final int index = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
+ if (index != AdapterView.INVALID_POSITION) {
+ mDestinationSpinner.setSelection(index);
+ return;
+ }
+ }
+ }
+
+ PrinterId printerId = mOldCurrentPrinter.getId();
+ final int index = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
+ mDestinationSpinner.setSelection(index);
+ }
+
+ private void startAdvancedPrintOptionsActivity(PrinterInfo printer) {
+ ComponentName serviceName = printer.getId().getServiceName();
+
+ String activityName = PrintOptionUtils.getAdvancedOptionsActivityName(this, serviceName);
+ if (TextUtils.isEmpty(activityName)) {
+ return;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(new ComponentName(serviceName.getPackageName(), activityName));
+
+ List<ResolveInfo> resolvedActivities = getPackageManager()
+ .queryIntentActivities(intent, 0);
+ if (resolvedActivities.isEmpty()) {
+ return;
+ }
+
+ // The activity is a component name, therefore it is one or none.
+ if (resolvedActivities.get(0).activityInfo.exported) {
+ intent.putExtra(PrintService.EXTRA_PRINT_JOB_INFO, mPrintJob);
+ intent.putExtra(PrintService.EXTRA_PRINTER_INFO, printer);
+
+ // This is external activity and may not be there.
+ try {
+ startActivityForResult(intent, ACTIVITY_REQUEST_POPULATE_ADVANCED_PRINT_OPTIONS);
+ } catch (ActivityNotFoundException anfe) {
+ Log.e(LOG_TAG, "Error starting activity for intent: " + intent, anfe);
+ }
+ }
+ }
+
+ private void onAdvancedPrintOptionsActivityResult(int resultCode, Intent data) {
+ if (resultCode != RESULT_OK || data == null) {
+ return;
+ }
+
+ PrintJobInfo printJobInfo = data.getParcelableExtra(PrintService.EXTRA_PRINT_JOB_INFO);
+
+ if (printJobInfo == null) {
+ return;
+ }
+
+ // Take the advanced options without interpretation.
+ mPrintJob.setAdvancedOptions(printJobInfo.getAdvancedOptions());
+
+ // Take copies without interpretation as the advanced print dialog
+ // cannot create a print job info with invalid copies.
+ mCopiesEditText.setText(String.valueOf(printJobInfo.getCopies()));
+ mPrintJob.setCopies(printJobInfo.getCopies());
+
+ PrintAttributes currAttributes = mPrintJob.getAttributes();
+ PrintAttributes newAttributes = printJobInfo.getAttributes();
+
+ // Take the media size only if the current printer supports is.
+ MediaSize oldMediaSize = currAttributes.getMediaSize();
+ MediaSize newMediaSize = newAttributes.getMediaSize();
+ if (!oldMediaSize.equals(newMediaSize)) {
+ final int mediaSizeCount = mMediaSizeSpinnerAdapter.getCount();
+ MediaSize newMediaSizePortrait = newAttributes.getMediaSize().asPortrait();
+ for (int i = 0; i < mediaSizeCount; i++) {
+ MediaSize supportedSizePortrait = mMediaSizeSpinnerAdapter.getItem(i).value.asPortrait();
+ if (supportedSizePortrait.equals(newMediaSizePortrait)) {
+ currAttributes.setMediaSize(newMediaSize);
+ mMediaSizeSpinner.setSelection(i);
+ if (currAttributes.getMediaSize().isPortrait()) {
+ if (mOrientationSpinner.getSelectedItemPosition() != 0) {
+ mOrientationSpinner.setSelection(0);
+ }
+ } else {
+ if (mOrientationSpinner.getSelectedItemPosition() != 1) {
+ mOrientationSpinner.setSelection(1);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // Take the color mode only if the current printer supports it.
+ final int currColorMode = currAttributes.getColorMode();
+ final int newColorMode = newAttributes.getColorMode();
+ if (currColorMode != newColorMode) {
+ final int colorModeCount = mColorModeSpinner.getCount();
+ for (int i = 0; i < colorModeCount; i++) {
+ final int supportedColorMode = mColorModeSpinnerAdapter.getItem(i).value;
+ if (supportedColorMode == newColorMode) {
+ currAttributes.setColorMode(newColorMode);
+ mColorModeSpinner.setSelection(i);
+ break;
+ }
+ }
+ }
+
+ // Take the page range only if it is valid.
+ PageRange[] pageRanges = printJobInfo.getPages();
+ if (pageRanges != null && pageRanges.length > 0) {
+ pageRanges = PageRangeUtils.normalize(pageRanges);
+
+ PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
+ final int pageCount = (info != null) ? info.getPageCount() : 0;
+
+ // Handle the case where all pages are specified explicitly
+ // instead of the *all pages* constant.
+ if (pageRanges.length == 1) {
+ if (pageRanges[0].getStart() == 0 && pageRanges[0].getEnd() == pageCount - 1) {
+ pageRanges[0] = PageRange.ALL_PAGES;
+ }
+ }
+
+ if (Arrays.equals(pageRanges, ALL_PAGES_ARRAY)) {
+ mPrintJob.setPages(pageRanges);
+
+ if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
+ mRangeOptionsSpinner.setSelection(0);
+ }
+ } else if (pageRanges[0].getStart() >= 0
+ && pageRanges[pageRanges.length - 1].getEnd() < pageCount) {
+ mPrintJob.setPages(pageRanges);
+
+ if (mRangeOptionsSpinner.getSelectedItemPosition() != 1) {
+ mRangeOptionsSpinner.setSelection(1);
+ }
+
+ StringBuilder builder = new StringBuilder();
+ final int pageRangeCount = pageRanges.length;
+ for (int i = 0; i < pageRangeCount; i++) {
+ if (builder.length() > 0) {
+ builder.append(',');
+ }
+
+ final int shownStartPage;
+ final int shownEndPage;
+ PageRange pageRange = pageRanges[i];
+ if (pageRange.equals(PageRange.ALL_PAGES)) {
+ shownStartPage = 1;
+ shownEndPage = pageCount;
+ } else {
+ shownStartPage = pageRange.getStart() + 1;
+ shownEndPage = pageRange.getEnd() + 1;
+ }
+
+ builder.append(shownStartPage);
+
+ if (shownStartPage != shownEndPage) {
+ builder.append('-');
+ builder.append(shownEndPage);
+ }
+ }
+ mPageRangeEditText.setText(builder.toString());
+ }
+ }
+
+ // Update the content if needed.
+ if (canUpdateDocument()) {
+ updateDocument(true, false);
+ }
+ }
+
+ private void ensureProgressUiShown() {
+ if (mUiState != UI_STATE_PROGRESS) {
+ mUiState = UI_STATE_PROGRESS;
+ Fragment fragment = PrintProgressFragment.newInstance();
+ showFragment(fragment);
+ }
+ }
+
+ private void ensurePreviewUiShown() {
+ if (mUiState != UI_STATE_PREVIEW) {
+ mUiState = UI_STATE_PREVIEW;
+ Fragment fragment = PrintPreviewFragment.newInstance();
+ showFragment(fragment);
+ }
+ }
+
+ private void ensureErrorUiShown(CharSequence message, int action) {
+ if (mUiState != UI_STATE_ERROR) {
+ mUiState = UI_STATE_ERROR;
+ Fragment fragment = PrintErrorFragment.newInstance(message, action);
+ showFragment(fragment);
+ }
+ }
+
+ private void showFragment(Fragment fragment) {
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ Fragment oldFragment = getFragmentManager().findFragmentById(
+ R.id.embedded_content_container);
+ if (oldFragment != null) {
+ transaction.remove(oldFragment);
+ }
+ transaction.add(R.id.embedded_content_container, fragment);
+ transaction.commit();
+ }
+
+ private void requestCreatePdfFileOrFinish() {
+ if (getCurrentPrinter() == mDestinationSpinnerAdapter.getPdfPrinter()) {
+ startCreateDocumentActivity();
+ } else {
+ finish();
+ }
+ }
+
+ private void finishIfConfirmedOrCanceled() {
+ if (mState == STATE_PRINT_CONFIRMED) {
+ requestCreatePdfFileOrFinish();
+ } else if (mState == STATE_PRINT_CANCELED) {
+ finish();
+ }
+ }
+
+ private void updatePrintAttributesFromCapabilities(PrinterCapabilitiesInfo capabilities) {
+ PrintAttributes defaults = capabilities.getDefaults();
+
+ // Sort the media sizes based on the current locale.
+ List<MediaSize> sortedMediaSizes = new ArrayList<>(capabilities.getMediaSizes());
+ Collections.sort(sortedMediaSizes, mMediaSizeComparator);
+
+ PrintAttributes attributes = mPrintJob.getAttributes();
+
+ // Media size.
+ MediaSize currMediaSize = attributes.getMediaSize();
+ if (currMediaSize == null) {
+ attributes.setMediaSize(defaults.getMediaSize());
+ } else {
+ boolean foundCurrentMediaSize = false;
+ // Try to find the current media size in the capabilities as
+ // it may be in a different orientation.
+ MediaSize currMediaSizePortrait = currMediaSize.asPortrait();
+ final int mediaSizeCount = sortedMediaSizes.size();
+ for (int i = 0; i < mediaSizeCount; i++) {
+ MediaSize mediaSize = sortedMediaSizes.get(i);
+ if (currMediaSizePortrait.equals(mediaSize.asPortrait())) {
+ attributes.setMediaSize(currMediaSize);
+ foundCurrentMediaSize = true;
+ break;
+ }
+ }
+ // If we did not find the current media size fall back to default.
+ if (!foundCurrentMediaSize) {
+ attributes.setMediaSize(defaults.getMediaSize());
+ }
+ }
+
+ // Color mode.
+ final int colorMode = attributes.getColorMode();
+ if ((capabilities.getColorModes() & colorMode) == 0) {
+ attributes.setColorMode(defaults.getColorMode());
+ }
+
+ // Resolution
+ Resolution resolution = attributes.getResolution();
+ if (resolution == null || !capabilities.getResolutions().contains(resolution)) {
+ attributes.setResolution(defaults.getResolution());
+ }
+
+ // Margins.
+ attributes.setMinMargins(defaults.getMinMargins());
+ }
+
+ private boolean updateDocument(boolean preview, boolean clearLastError) {
+ if (!clearLastError && mPrintedDocument.hasUpdateError()) {
+ return false;
+ }
+
+ if (clearLastError && mPrintedDocument.hasUpdateError()) {
+ mPrintedDocument.clearUpdateError();
+ }
+
+ if (mRequestedPages != null && mRequestedPages.length > 0) {
+ final PageRange[] pages;
+ if (preview) {
+ final int firstPage = mRequestedPages[0].getStart();
+ pages = new PageRange[]{new PageRange(firstPage, firstPage)};
+ } else {
+ pages = mRequestedPages;
+ }
+ final boolean willUpdate = mPrintedDocument.update(mPrintJob.getAttributes(),
+ pages, preview);
+
+ if (willUpdate) {
+ mProgressMessageController.post();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void addCurrentPrinterToHistory() {
+ PrinterInfo currentPrinter = getCurrentPrinter();
+ if (currentPrinter != null) {
+ PrinterId fakePdfPrinterId = mDestinationSpinnerAdapter.getPdfPrinter().getId();
+ if (!currentPrinter.getId().equals(fakePdfPrinterId)) {
+ mPrinterRegistry.addHistoricalPrinter(currentPrinter);
+ }
+ }
+ }
+
+ private PrinterInfo getCurrentPrinter() {
+ return ((PrinterHolder) mDestinationSpinner.getSelectedItem()).printer;
+ }
+
+ private void cancelPrint() {
+ mState = STATE_PRINT_CANCELED;
+ updateOptionsUi();
+ if (mPrintedDocument.isUpdating()) {
+ mPrintedDocument.cancel();
+ }
+ finish();
+ }
+
+ private void confirmPrint() {
+ mState = STATE_PRINT_CONFIRMED;
+ updateOptionsUi();
+ if (canUpdateDocument()) {
+ updateDocument(false, false);
+ }
+ addCurrentPrinterToHistory();
+ if (!mPrintedDocument.isUpdating()) {
+ requestCreatePdfFileOrFinish();
+ }
+ }
+
+ private void bindUi() {
+ // Summary
+ mSummaryCopies = (TextView) findViewById(R.id.copies_count_summary);
+ mSummaryPaperSize = (TextView) findViewById(R.id.paper_size_summary);
+
+ // Options container
+ mOptionsContent = (ContentView) findViewById(R.id.options_content);
+ mOptionsContent.setOptionsStateChangeListener(new OptionsStateChangeListener() {
+ @Override
+ public void onOptionsOpened() {
+ // TODO: Update preview.
+ }
+
+ @Override
+ public void onOptionsClosed() {
+ // TODO: Update preview.
+ }
+ });
+
+ OnItemSelectedListener itemSelectedListener = new MyOnItemSelectedListener();
+ OnClickListener clickListener = new MyClickListener();
+
+ // Copies
+ mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
+ mCopiesEditText.setOnFocusChangeListener(mSelectAllOnFocusListener);
+ mCopiesEditText.setText(MIN_COPIES_STRING);
+ mCopiesEditText.setSelection(mCopiesEditText.getText().length());
+ mCopiesEditText.addTextChangedListener(new EditTextWatcher());
+
+ // Destination.
+ mDestinationSpinnerAdapter.registerDataSetObserver(new PrintersObserver());
+ mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
+ mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
+ mDestinationSpinner.setOnItemSelectedListener(itemSelectedListener);
+ mDestinationSpinner.setSelection(0);
+
+ // Media size.
+ mMediaSizeSpinnerAdapter = new ArrayAdapter<>(
+ this, R.layout.spinner_dropdown_item, R.id.title);
+ mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
+ mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
+ mMediaSizeSpinner.setOnItemSelectedListener(itemSelectedListener);
+
+ // Color mode.
+ mColorModeSpinnerAdapter = new ArrayAdapter<>(
+ this, R.layout.spinner_dropdown_item, R.id.title);
+ mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
+ mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
+ mColorModeSpinner.setOnItemSelectedListener(itemSelectedListener);
+
+ // Orientation
+ mOrientationSpinnerAdapter = new ArrayAdapter<>(
+ this, R.layout.spinner_dropdown_item, R.id.title);
+ String[] orientationLabels = getResources().getStringArray(
+ R.array.orientation_labels);
+ mOrientationSpinnerAdapter.add(new SpinnerItem<>(
+ ORIENTATION_PORTRAIT, orientationLabels[0]));
+ mOrientationSpinnerAdapter.add(new SpinnerItem<>(
+ ORIENTATION_LANDSCAPE, orientationLabels[1]));
+ mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
+ mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
+ mOrientationSpinner.setOnItemSelectedListener(itemSelectedListener);
+
+ // Range options
+ ArrayAdapter<SpinnerItem<Integer>> rangeOptionsSpinnerAdapter =
+ new ArrayAdapter<>(this, R.layout.spinner_dropdown_item, R.id.title);
+ final int[] rangeOptionsValues = getResources().getIntArray(
+ R.array.page_options_values);
+ String[] rangeOptionsLabels = getResources().getStringArray(
+ R.array.page_options_labels);
+ final int rangeOptionsCount = rangeOptionsLabels.length;
+ for (int i = 0; i < rangeOptionsCount; i++) {
+ rangeOptionsSpinnerAdapter.add(new SpinnerItem<>(
+ rangeOptionsValues[i], rangeOptionsLabels[i]));
+ }
+ mPageRangeOptionsTitle = (TextView) findViewById(R.id.range_options_title);
+ mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
+ mRangeOptionsSpinner.setAdapter(rangeOptionsSpinnerAdapter);
+ mRangeOptionsSpinner.setOnItemSelectedListener(itemSelectedListener);
+
+ // Page range
+ mPageRangeTitle = (TextView) findViewById(R.id.page_range_title);
+ mPageRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
+ mPageRangeEditText.setOnFocusChangeListener(mSelectAllOnFocusListener);
+ mPageRangeEditText.addTextChangedListener(new RangeTextWatcher());
+
+ // Advanced options button.
+ mAdvancedPrintOptionsContainer = findViewById(R.id.more_options_container);
+ mMoreOptionsButton = (Button) findViewById(R.id.more_options_button);
+ mMoreOptionsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ PrinterInfo currentPrinter = getCurrentPrinter();
+ if (currentPrinter != null) {
+ startAdvancedPrintOptionsActivity(currentPrinter);
+ }
+ }
+ });
+
+ // Print button
+ mPrintButton = (ImageView) findViewById(R.id.print_button);
+ mPrintButton.setOnClickListener(clickListener);
+ }
+
+ private final class MyClickListener implements OnClickListener {
+ @Override
+ public void onClick(View view) {
+ if (view == mPrintButton) {
+ PrinterInfo currentPrinter = getCurrentPrinter();
+ if (currentPrinter != null) {
+ confirmPrint();
+ } else {
+ cancelPrint();
+ }
+ } else if (view == mMoreOptionsButton) {
+ PrinterInfo currentPrinter = getCurrentPrinter();
+ if (currentPrinter != null) {
+ startAdvancedPrintOptionsActivity(currentPrinter);
+ }
+ }
+ }
+ }
+
+ private static boolean canPrint(PrinterInfo printer) {
+ return printer.getCapabilities() != null
+ && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+ }
+
+ private void updateOptionsUi() {
+ // Always update the summary.
+ if (!TextUtils.isEmpty(mCopiesEditText.getText())) {
+ mSummaryCopies.setText(mCopiesEditText.getText());
+ }
+
+ final int selectedMediaIndex = mMediaSizeSpinner.getSelectedItemPosition();
+ if (selectedMediaIndex >= 0) {
+ SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(selectedMediaIndex);
+ mSummaryPaperSize.setText(mediaItem.label);
+ }
+
+ if (mState == STATE_PRINT_CONFIRMED
+ || mState == STATE_PRINT_CANCELED
+ || mState == STATE_UPDATE_FAILED
+ || mState == STATE_CREATE_FILE_FAILED
+ || mState == STATE_PRINTER_UNAVAILABLE) {
+ if (mState != STATE_PRINTER_UNAVAILABLE) {
+ mDestinationSpinner.setEnabled(false);
+ }
+ mCopiesEditText.setEnabled(false);
+ mMediaSizeSpinner.setEnabled(false);
+ mColorModeSpinner.setEnabled(false);
+ mOrientationSpinner.setEnabled(false);
+ mRangeOptionsSpinner.setEnabled(false);
+ mPageRangeEditText.setEnabled(false);
+ mPrintButton.setEnabled(false);
+ mMoreOptionsButton.setEnabled(false);
+ return;
+ }
+
+ // If no current printer, or it has no capabilities, or it is not
+ // available, we disable all print options except the destination.
+ PrinterInfo currentPrinter = getCurrentPrinter();
+ if (currentPrinter == null || !canPrint(currentPrinter)) {
+ mCopiesEditText.setEnabled(false);
+ mMediaSizeSpinner.setEnabled(false);
+ mColorModeSpinner.setEnabled(false);
+ mOrientationSpinner.setEnabled(false);
+ mRangeOptionsSpinner.setEnabled(false);
+ mPageRangeEditText.setEnabled(false);
+ mPrintButton.setEnabled(false);
+ mMoreOptionsButton.setEnabled(false);
+ return;
+ }
+
+ PrinterCapabilitiesInfo capabilities = currentPrinter.getCapabilities();
+ PrintAttributes defaultAttributes = capabilities.getDefaults();
+
+ // Media size.
+ mMediaSizeSpinner.setEnabled(true);
+
+ List<MediaSize> mediaSizes = new ArrayList<>(capabilities.getMediaSizes());
+ // Sort the media sizes based on the current locale.
+ Collections.sort(mediaSizes, mMediaSizeComparator);
+
+ PrintAttributes attributes = mPrintJob.getAttributes();
+
+ // If the media sizes changed, we update the adapter and the spinner.
+ boolean mediaSizesChanged = false;
+ final int mediaSizeCount = mediaSizes.size();
+ if (mediaSizeCount != mMediaSizeSpinnerAdapter.getCount()) {
+ mediaSizesChanged = true;
+ } else {
+ for (int i = 0; i < mediaSizeCount; i++) {
+ if (!mediaSizes.get(i).equals(mMediaSizeSpinnerAdapter.getItem(i).value)) {
+ mediaSizesChanged = true;
+ break;
+ }
+ }
+ }
+ if (mediaSizesChanged) {
+ // Remember the old media size to try selecting it again.
+ int oldMediaSizeNewIndex = AdapterView.INVALID_POSITION;
+ MediaSize oldMediaSize = attributes.getMediaSize();
+
+ // Rebuild the adapter data.
+ mMediaSizeSpinnerAdapter.clear();
+ for (int i = 0; i < mediaSizeCount; i++) {
+ MediaSize mediaSize = mediaSizes.get(i);
+ if (oldMediaSize != null
+ && mediaSize.asPortrait().equals(oldMediaSize.asPortrait())) {
+ // Update the index of the old selection.
+ oldMediaSizeNewIndex = i;
+ }
+ mMediaSizeSpinnerAdapter.add(new SpinnerItem<>(
+ mediaSize, mediaSize.getLabel(getPackageManager())));
+ }
+
+ if (oldMediaSizeNewIndex != AdapterView.INVALID_POSITION) {
+ // Select the old media size - nothing really changed.
+ if (mMediaSizeSpinner.getSelectedItemPosition() != oldMediaSizeNewIndex) {
+ mMediaSizeSpinner.setSelection(oldMediaSizeNewIndex);
+ }
+ } else {
+ // Select the first or the default.
+ final int mediaSizeIndex = Math.max(mediaSizes.indexOf(
+ defaultAttributes.getMediaSize()), 0);
+ if (mMediaSizeSpinner.getSelectedItemPosition() != mediaSizeIndex) {
+ mMediaSizeSpinner.setSelection(mediaSizeIndex);
+ }
+ // Respect the orientation of the old selection.
+ if (oldMediaSize != null) {
+ if (oldMediaSize.isPortrait()) {
+ attributes.setMediaSize(mMediaSizeSpinnerAdapter
+ .getItem(mediaSizeIndex).value.asPortrait());
+ } else {
+ attributes.setMediaSize(mMediaSizeSpinnerAdapter
+ .getItem(mediaSizeIndex).value.asLandscape());
+ }
+ }
+ }
+ }
+
+ // Color mode.
+ mColorModeSpinner.setEnabled(true);
+ final int colorModes = capabilities.getColorModes();
+
+ // If the color modes changed, we update the adapter and the spinner.
+ boolean colorModesChanged = false;
+ if (Integer.bitCount(colorModes) != mColorModeSpinnerAdapter.getCount()) {
+ colorModesChanged = true;
+ } else {
+ int remainingColorModes = colorModes;
+ int adapterIndex = 0;
+ while (remainingColorModes != 0) {
+ final int colorBitOffset = Integer.numberOfTrailingZeros(remainingColorModes);
+ final int colorMode = 1 << colorBitOffset;
+ remainingColorModes &= ~colorMode;
+ if (colorMode != mColorModeSpinnerAdapter.getItem(adapterIndex).value) {
+ colorModesChanged = true;
+ break;
+ }
+ adapterIndex++;
+ }
+ }
+ if (colorModesChanged) {
+ // Remember the old color mode to try selecting it again.
+ int oldColorModeNewIndex = AdapterView.INVALID_POSITION;
+ final int oldColorMode = attributes.getColorMode();
+
+ // Rebuild the adapter data.
+ mColorModeSpinnerAdapter.clear();
+ String[] colorModeLabels = getResources().getStringArray(R.array.color_mode_labels);
+ int remainingColorModes = colorModes;
+ while (remainingColorModes != 0) {
+ final int colorBitOffset = Integer.numberOfTrailingZeros(remainingColorModes);
+ final int colorMode = 1 << colorBitOffset;
+ if (colorMode == oldColorMode) {
+ // Update the index of the old selection.
+ oldColorModeNewIndex = colorBitOffset;
+ }
+ remainingColorModes &= ~colorMode;
+ mColorModeSpinnerAdapter.add(new SpinnerItem<>(colorMode,
+ colorModeLabels[colorBitOffset]));
+ }
+ if (oldColorModeNewIndex != AdapterView.INVALID_POSITION) {
+ // Select the old color mode - nothing really changed.
+ if (mColorModeSpinner.getSelectedItemPosition() != oldColorModeNewIndex) {
+ mColorModeSpinner.setSelection(oldColorModeNewIndex);
+ }
+ } else {
+ // Select the default.
+ final int selectedColorMode = colorModes & defaultAttributes.getColorMode();
+ final int itemCount = mColorModeSpinnerAdapter.getCount();
+ for (int i = 0; i < itemCount; i++) {
+ SpinnerItem<Integer> item = mColorModeSpinnerAdapter.getItem(i);
+ if (selectedColorMode == item.value) {
+ if (mColorModeSpinner.getSelectedItemPosition() != i) {
+ mColorModeSpinner.setSelection(i);
+ }
+ attributes.setColorMode(selectedColorMode);
+ }
+ }
+ }
+ }
+
+ // Orientation
+ mOrientationSpinner.setEnabled(true);
+ MediaSize mediaSize = attributes.getMediaSize();
+ if (mediaSize != null) {
+ if (mediaSize.isPortrait()
+ && mOrientationSpinner.getSelectedItemPosition() != 0) {
+ mOrientationSpinner.setSelection(0);
+ } else if (!mediaSize.isPortrait()
+ && mOrientationSpinner.getSelectedItemPosition() != 1) {
+ mOrientationSpinner.setSelection(1);
+ }
+ }
+
+ // Range options
+ PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
+ if (info != null && info.getPageCount() > 0) {
+ if (info.getPageCount() == 1) {
+ mRangeOptionsSpinner.setEnabled(false);
+ } else {
+ mRangeOptionsSpinner.setEnabled(true);
+ if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
+ if (!mPageRangeEditText.isEnabled()) {
+ mPageRangeEditText.setEnabled(true);
+ mPageRangeEditText.setVisibility(View.VISIBLE);
+ mPageRangeTitle.setVisibility(View.VISIBLE);
+ mPageRangeEditText.requestFocus();
+ InputMethodManager imm = (InputMethodManager)
+ getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mPageRangeEditText, 0);
+ }
+ } else {
+ mPageRangeEditText.setEnabled(false);
+ mPageRangeEditText.setVisibility(View.INVISIBLE);
+ mPageRangeTitle.setVisibility(View.INVISIBLE);
+ }
+ }
+ String title = (info.getPageCount() != PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
+ ? getString(R.string.label_pages, String.valueOf(info.getPageCount()))
+ : getString(R.string.page_count_unknown);
+ mPageRangeOptionsTitle.setText(title);
+ } else {
+ if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
+ mRangeOptionsSpinner.setSelection(0);
+ }
+ mRangeOptionsSpinner.setEnabled(false);
+ mPageRangeOptionsTitle.setText(getString(R.string.page_count_unknown));
+ mPageRangeEditText.setEnabled(false);
+ mPageRangeEditText.setVisibility(View.INVISIBLE);
+ mPageRangeTitle.setVisibility(View.INVISIBLE);
+ }
+
+ // Advanced print options
+ ComponentName serviceName = currentPrinter.getId().getServiceName();
+ if (!TextUtils.isEmpty(PrintOptionUtils.getAdvancedOptionsActivityName(
+ this, serviceName))) {
+ mAdvancedPrintOptionsContainer.setVisibility(View.VISIBLE);
+ mMoreOptionsButton.setEnabled(true);
+ } else {
+ mAdvancedPrintOptionsContainer.setVisibility(View.GONE);
+ mMoreOptionsButton.setEnabled(false);
+ }
+
+ // Print
+ if (mDestinationSpinnerAdapter.getPdfPrinter() != currentPrinter) {
+ mPrintButton.setImageResource(com.android.internal.R.drawable.ic_print);
+ } else {
+ mPrintButton.setImageResource(com.android.internal.R.drawable.ic_menu_save);
+ }
+ if ((mRangeOptionsSpinner.getSelectedItemPosition() == 1
+ && (TextUtils.isEmpty(mPageRangeEditText.getText()) || hasErrors()))
+ || (mRangeOptionsSpinner.getSelectedItemPosition() == 0
+ && (mPrintedDocument.getDocumentInfo() == null || hasErrors()))) {
+ mPrintButton.setEnabled(false);
+ } else {
+ mPrintButton.setEnabled(true);
+ }
+
+ // Copies
+ if (mDestinationSpinnerAdapter.getPdfPrinter() != currentPrinter) {
+ mCopiesEditText.setEnabled(true);
+ } else {
+ mCopiesEditText.setEnabled(false);
+ }
+ if (mCopiesEditText.getError() == null
+ && TextUtils.isEmpty(mCopiesEditText.getText())) {
+ mCopiesEditText.setText(String.valueOf(MIN_COPIES));
+ mCopiesEditText.requestFocus();
+ }
+ }
+
+ private PageRange[] computeRequestedPages() {
+ if (hasErrors()) {
+ return null;
+ }
+
+ if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
+ List<PageRange> pageRanges = new ArrayList<>();
+ mStringCommaSplitter.setString(mPageRangeEditText.getText().toString());
+
+ while (mStringCommaSplitter.hasNext()) {
+ String range = mStringCommaSplitter.next().trim();
+ if (TextUtils.isEmpty(range)) {
+ continue;
+ }
+ final int dashIndex = range.indexOf('-');
+ final int fromIndex;
+ final int toIndex;
+
+ if (dashIndex > 0) {
+ fromIndex = Integer.parseInt(range.substring(0, dashIndex).trim()) - 1;
+ // It is possible that the dash is at the end since the input
+ // verification can has to allow the user to keep entering if
+ // this would lead to a valid input. So we handle this.
+ if (dashIndex < range.length() - 1) {
+ String fromString = range.substring(dashIndex + 1, range.length()).trim();
+ toIndex = Integer.parseInt(fromString) - 1;
+ } else {
+ toIndex = fromIndex;
+ }
+ } else {
+ fromIndex = toIndex = Integer.parseInt(range) - 1;
+ }
+
+ PageRange pageRange = new PageRange(Math.min(fromIndex, toIndex),
+ Math.max(fromIndex, toIndex));
+ pageRanges.add(pageRange);
+ }
+
+ PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
+ pageRanges.toArray(pageRangesArray);
+
+ return PageRangeUtils.normalize(pageRangesArray);
+ }
+
+ return ALL_PAGES_ARRAY;
+ }
+
+ private boolean hasErrors() {
+ return (mCopiesEditText.getError() != null)
+ || (mPageRangeEditText.getVisibility() == View.VISIBLE
+ && mPageRangeEditText.getError() != null);
+ }
+
+ public void onPrinterAvailable(PrinterInfo printer) {
+ PrinterInfo currentPrinter = getCurrentPrinter();
+ if (currentPrinter.equals(printer)) {
+ mState = STATE_CONFIGURING;
+ if (canUpdateDocument()) {
+ updateDocument(true, false);
+ }
+ ensurePreviewUiShown();
+ updateOptionsUi();
+ }
+ }
+
+ public void onPrinterUnavailable(PrinterInfo printer) {
+ if (getCurrentPrinter().getId().equals(printer.getId())) {
+ mState = STATE_PRINTER_UNAVAILABLE;
+ if (mPrintedDocument.isUpdating()) {
+ mPrintedDocument.cancel();
+ }
+ ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
+ PrintErrorFragment.ACTION_NONE);
+ updateOptionsUi();
+ }
+ }
+
+ private final class SpinnerItem<T> {
+ final T value;
+ final CharSequence label;
+
+ public SpinnerItem(T value, CharSequence label) {
+ this.value = value;
+ this.label = label;
+ }
+
+ public String toString() {
+ return label.toString();
+ }
+ }
+
+ private final class PrinterAvailabilityDetector implements Runnable {
+ private static final long UNAVAILABLE_TIMEOUT_MILLIS = 10000; // 10sec
+
+ private boolean mPosted;
+
+ private boolean mPrinterUnavailable;
+
+ private PrinterInfo mPrinter;
+
+ public void updatePrinter(PrinterInfo printer) {
+ if (printer.equals(mDestinationSpinnerAdapter.getPdfPrinter())) {
+ return;
+ }
+
+ final boolean available = printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE
+ && printer.getCapabilities() != null;
+ final boolean notifyIfAvailable;
+
+ if (mPrinter == null || !mPrinter.getId().equals(printer.getId())) {
+ notifyIfAvailable = true;
+ unpostIfNeeded();
+ mPrinterUnavailable = false;
+ mPrinter = new PrinterInfo.Builder(printer).build();
+ } else {
+ notifyIfAvailable =
+ (mPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE
+ && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE)
+ || (mPrinter.getCapabilities() == null
+ && printer.getCapabilities() != null);
+ mPrinter.copyFrom(printer);
+ }
+
+ if (available) {
+ unpostIfNeeded();
+ mPrinterUnavailable = false;
+ if (notifyIfAvailable) {
+ onPrinterAvailable(mPrinter);
+ }
+ } else {
+ if (!mPrinterUnavailable) {
+ postIfNeeded();
+ }
+ }
+ }
+
+ public void cancel() {
+ unpostIfNeeded();
+ mPrinterUnavailable = false;
+ }
+
+ private void postIfNeeded() {
+ if (!mPosted) {
+ mPosted = true;
+ mDestinationSpinner.postDelayed(this, UNAVAILABLE_TIMEOUT_MILLIS);
+ }
+ }
+
+ private void unpostIfNeeded() {
+ if (mPosted) {
+ mPosted = false;
+ mDestinationSpinner.removeCallbacks(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ mPosted = false;
+ mPrinterUnavailable = true;
+ onPrinterUnavailable(mPrinter);
+ }
+ }
+
+ private static final class PrinterHolder {
+ PrinterInfo printer;
+ boolean removed;
+
+ public PrinterHolder(PrinterInfo printer) {
+ this.printer = printer;
+ }
+ }
+
+ private final class DestinationAdapter extends BaseAdapter
+ implements PrinterRegistry.OnPrintersChangeListener {
+ private final List<PrinterHolder> mPrinterHolders = new ArrayList<>();
+
+ private final PrinterHolder mFakePdfPrinterHolder;
+
+ public DestinationAdapter() {
+ addPrinters(mPrinterHolders, mPrinterRegistry.getPrinters());
+ mPrinterRegistry.setOnPrintersChangeListener(this);
+ mFakePdfPrinterHolder = new PrinterHolder(createFakePdfPrinter());
+ }
+
+ public PrinterInfo getPdfPrinter() {
+ return mFakePdfPrinterHolder.printer;
+ }
+
+ public int getPrinterIndex(PrinterId printerId) {
+ for (int i = 0; i < getCount(); i++) {
+ PrinterHolder printerHolder = (PrinterHolder) getItem(i);
+ if (printerHolder != null && !printerHolder.removed
+ && printerHolder.printer.getId().equals(printerId)) {
+ return i;
+ }
+ }
+ return AdapterView.INVALID_POSITION;
+ }
+
+ public void ensurePrinterInVisibleAdapterPosition(PrinterId printerId) {
+ final int printerCount = mPrinterHolders.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterHolder printerHolder = mPrinterHolders.get(i);
+ if (printerHolder.printer.getId().equals(printerId)) {
+ // If already in the list - do nothing.
+ if (i < getCount() - 2) {
+ return;
+ }
+ // Else replace the last one (two items are not printers).
+ final int lastPrinterIndex = getCount() - 3;
+ mPrinterHolders.set(i, mPrinterHolders.get(lastPrinterIndex));
+ mPrinterHolders.set(lastPrinterIndex, printerHolder);
+ notifyDataSetChanged();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return Math.min(mPrinterHolders.size() + 2, DEST_ADAPTER_MAX_ITEM_COUNT);
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ Object item = getItem(position);
+ if (item instanceof PrinterHolder) {
+ PrinterHolder printerHolder = (PrinterHolder) item;
+ return !printerHolder.removed
+ && printerHolder.printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+ }
+ return true;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (mPrinterHolders.isEmpty()) {
+ if (position == 0) {
+ return mFakePdfPrinterHolder;
+ }
+ } else {
+ if (position < 1) {
+ return mPrinterHolders.get(position);
+ }
+ if (position == 1) {
+ return mFakePdfPrinterHolder;
+ }
+ if (position < getCount() - 1) {
+ return mPrinterHolders.get(position - 1);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (mPrinterHolders.isEmpty()) {
+ if (position == 0) {
+ return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
+ } else if (position == 1) {
+ return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
+ }
+ } else {
+ if (position == 1) {
+ return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
+ }
+ if (position == getCount() - 1) {
+ return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
+ }
+ }
+ return position;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ View view = getView(position, convertView, parent);
+ view.setEnabled(isEnabled(position));
+ return view;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.printer_dropdown_item, parent, false);
+ }
+
+ CharSequence title = null;
+ CharSequence subtitle = null;
+ Drawable icon = null;
+
+ if (mPrinterHolders.isEmpty()) {
+ if (position == 0 && getPdfPrinter() != null) {
+ PrinterHolder printerHolder = (PrinterHolder) getItem(position);
+ title = printerHolder.printer.getName();
+ icon = getResources().getDrawable(com.android.internal.R.drawable.ic_menu_save);
+ } else if (position == 1) {
+ title = getString(R.string.all_printers);
+ }
+ } else {
+ if (position == 1 && getPdfPrinter() != null) {
+ PrinterHolder printerHolder = (PrinterHolder) getItem(position);
+ title = printerHolder.printer.getName();
+ icon = getResources().getDrawable(com.android.internal.R.drawable.ic_menu_save);
+ } else if (position == getCount() - 1) {
+ title = getString(R.string.all_printers);
+ } else {
+ PrinterHolder printerHolder = (PrinterHolder) getItem(position);
+ title = printerHolder.printer.getName();
+ try {
+ PackageInfo packageInfo = getPackageManager().getPackageInfo(
+ printerHolder.printer.getId().getServiceName().getPackageName(), 0);
+ subtitle = packageInfo.applicationInfo.loadLabel(getPackageManager());
+ icon = packageInfo.applicationInfo.loadIcon(getPackageManager());
+ } catch (NameNotFoundException nnfe) {
+ /* ignore */
+ }
+ }
+ }
+
+ TextView titleView = (TextView) convertView.findViewById(R.id.title);
+ titleView.setText(title);
+
+ TextView subtitleView = (TextView) convertView.findViewById(R.id.subtitle);
+ if (!TextUtils.isEmpty(subtitle)) {
+ subtitleView.setText(subtitle);
+ subtitleView.setVisibility(View.VISIBLE);
+ } else {
+ subtitleView.setText(null);
+ subtitleView.setVisibility(View.GONE);
+ }
+
+ ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
+ if (icon != null) {
+ iconView.setImageDrawable(icon);
+ iconView.setVisibility(View.VISIBLE);
+ } else {
+ iconView.setVisibility(View.INVISIBLE);
+ }
+
+ return convertView;
+ }
+
+ @Override
+ public void onPrintersChanged(List<PrinterInfo> printers) {
+ // We rearrange the printers if the user selects a printer
+ // not shown in the initial short list. Therefore, we have
+ // to keep the printer order.
+
+ // No old printers - do not bother keeping their position.
+ if (mPrinterHolders.isEmpty()) {
+ addPrinters(mPrinterHolders, printers);
+ notifyDataSetChanged();
+ return;
+ }
+
+ // Add the new printers to a map.
+ ArrayMap<PrinterId, PrinterInfo> newPrintersMap = new ArrayMap<>();
+ final int printerCount = printers.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo printer = printers.get(i);
+ newPrintersMap.put(printer.getId(), printer);
+ }
+
+ List<PrinterHolder> newPrinterHolders = new ArrayList<>();
+
+ // Update printers we already have which are either updated or removed.
+ // We do not remove printers if the currently selected printer is removed
+ // to prevent the user printing to a wrong printer.
+ final int oldPrinterCount = mPrinterHolders.size();
+ for (int i = 0; i < oldPrinterCount; i++) {
+ PrinterHolder printerHolder = mPrinterHolders.get(i);
+ PrinterId oldPrinterId = printerHolder.printer.getId();
+ PrinterInfo updatedPrinter = newPrintersMap.remove(oldPrinterId);
+ if (updatedPrinter != null) {
+ printerHolder.printer = updatedPrinter;
+ } else {
+ printerHolder.removed = true;
+ }
+ newPrinterHolders.add(printerHolder);
+ }
+
+ // Add the rest of the new printers, i.e. what is left.
+ addPrinters(newPrinterHolders, newPrintersMap.values());
+
+ mPrinterHolders.clear();
+ mPrinterHolders.addAll(newPrinterHolders);
+
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onPrintersInvalid() {
+ mPrinterHolders.clear();
+ notifyDataSetInvalidated();
+ }
+
+ public PrinterHolder getPrinterHolder(PrinterId printerId) {
+ final int itemCount = getCount();
+ for (int i = 0; i < itemCount; i++) {
+ Object item = getItem(i);
+ if (item instanceof PrinterHolder) {
+ PrinterHolder printerHolder = (PrinterHolder) item;
+ if (printerId.equals(printerHolder.printer.getId())) {
+ return printerHolder;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void pruneRemovedPrinters() {
+ final int holderCounts = mPrinterHolders.size();
+ for (int i = holderCounts - 1; i >= 0; i--) {
+ PrinterHolder printerHolder = mPrinterHolders.get(i);
+ if (printerHolder.removed) {
+ mPrinterHolders.remove(i);
+ }
+ }
+ }
+
+ private void addPrinters(List<PrinterHolder> list, Collection<PrinterInfo> printers) {
+ for (PrinterInfo printer : printers) {
+ PrinterHolder printerHolder = new PrinterHolder(printer);
+ list.add(printerHolder);
+ }
+ }
+
+ private PrinterInfo createFakePdfPrinter() {
+ MediaSize defaultMediaSize = MediaSizeUtils.getDefault(PrintActivity.this);
+
+ PrinterId printerId = new PrinterId(getComponentName(), "PDF printer");
+
+ PrinterCapabilitiesInfo.Builder builder =
+ new PrinterCapabilitiesInfo.Builder(printerId);
+
+ String[] mediaSizeIds = getResources().getStringArray(R.array.pdf_printer_media_sizes);
+ final int mediaSizeIdCount = mediaSizeIds.length;
+ for (int i = 0; i < mediaSizeIdCount; i++) {
+ String id = mediaSizeIds[i];
+ MediaSize mediaSize = MediaSize.getStandardMediaSizeById(id);
+ builder.addMediaSize(mediaSize, mediaSize.equals(defaultMediaSize));
+ }
+
+ builder.addResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300),
+ true);
+ builder.setColorModes(PrintAttributes.COLOR_MODE_COLOR
+ | PrintAttributes.COLOR_MODE_MONOCHROME, PrintAttributes.COLOR_MODE_COLOR);
+
+ return new PrinterInfo.Builder(printerId, getString(R.string.save_as_pdf),
+ PrinterInfo.STATUS_IDLE).setCapabilities(builder.build()).build();
+ }
+ }
+
+ private final class PrintersObserver extends DataSetObserver {
+ @Override
+ public void onChanged() {
+ PrinterInfo oldPrinterState = mOldCurrentPrinter;
+ if (oldPrinterState == null) {
+ return;
+ }
+
+ PrinterHolder printerHolder = mDestinationSpinnerAdapter.getPrinterHolder(
+ oldPrinterState.getId());
+ if (printerHolder == null) {
+ return;
+ }
+ PrinterInfo newPrinterState = printerHolder.printer;
+
+ if (!printerHolder.removed) {
+ mDestinationSpinnerAdapter.pruneRemovedPrinters();
+ } else {
+ onPrinterUnavailable(newPrinterState);
+ }
+
+ if (oldPrinterState.equals(newPrinterState)) {
+ return;
+ }
+
+ PrinterCapabilitiesInfo oldCapab = oldPrinterState.getCapabilities();
+ PrinterCapabilitiesInfo newCapab = newPrinterState.getCapabilities();
+
+ final boolean hasCapab = newCapab != null;
+ final boolean gotCapab = oldCapab == null && newCapab != null;
+ final boolean lostCapab = oldCapab != null && newCapab == null;
+ final boolean capabChanged = capabilitiesChanged(oldCapab, newCapab);
+
+ final int oldStatus = oldPrinterState.getStatus();
+ final int newStatus = newPrinterState.getStatus();
+
+ final boolean isActive = newStatus != PrinterInfo.STATUS_UNAVAILABLE;
+ final boolean becameActive = (oldStatus == PrinterInfo.STATUS_UNAVAILABLE
+ && oldStatus != newStatus);
+ final boolean becameInactive = (newStatus == PrinterInfo.STATUS_UNAVAILABLE
+ && oldStatus != newStatus);
+
+ mPrinterAvailabilityDetector.updatePrinter(newPrinterState);
+
+ oldPrinterState.copyFrom(newPrinterState);
+
+ if ((isActive && gotCapab) || (becameActive && hasCapab)) {
+ onPrinterAvailable(newPrinterState);
+ } else if ((becameInactive && hasCapab)|| (isActive && lostCapab)) {
+ onPrinterUnavailable(newPrinterState);
+ }
+
+ if (hasCapab && capabChanged) {
+ updatePrintAttributesFromCapabilities(newCapab);
+ }
+
+ final boolean updateNeeded = ((capabChanged && hasCapab && isActive)
+ || (becameActive && hasCapab) || (isActive && gotCapab));
+
+ if (updateNeeded && canUpdateDocument()) {
+ updateDocument(true, false);
+ }
+
+ updateOptionsUi();
+ }
+
+ private boolean capabilitiesChanged(PrinterCapabilitiesInfo oldCapabilities,
+ PrinterCapabilitiesInfo newCapabilities) {
+ if (oldCapabilities == null) {
+ if (newCapabilities != null) {
+ return true;
+ }
+ } else if (!oldCapabilities.equals(newCapabilities)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private final class MyOnItemSelectedListener implements AdapterView.OnItemSelectedListener {
+ @Override
+ public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {
+ if (spinner == mDestinationSpinner) {
+ if (position == AdapterView.INVALID_POSITION) {
+ return;
+ }
+
+ if (id == DEST_ADAPTER_ITEM_ID_ALL_PRINTERS) {
+ startSelectPrinterActivity();
+ return;
+ }
+
+ PrinterInfo currentPrinter = getCurrentPrinter();
+
+ // Why on earth item selected is called if no selection changed.
+ if (mOldCurrentPrinter == currentPrinter) {
+ return;
+ }
+ mOldCurrentPrinter = currentPrinter;
+
+ PrinterHolder printerHolder = mDestinationSpinnerAdapter.getPrinterHolder(
+ currentPrinter.getId());
+ if (!printerHolder.removed) {
+ mDestinationSpinnerAdapter.pruneRemovedPrinters();
+ ensurePreviewUiShown();
+ }
+
+ mPrintJob.setPrinterId(currentPrinter.getId());
+ mPrintJob.setPrinterName(currentPrinter.getName());
+
+ mPrinterRegistry.setTrackedPrinter(currentPrinter.getId());
+
+ PrinterCapabilitiesInfo capabilities = currentPrinter.getCapabilities();
+ if (capabilities != null) {
+ updatePrintAttributesFromCapabilities(capabilities);
+ }
+
+ mPrinterAvailabilityDetector.updatePrinter(currentPrinter);
+ } else if (spinner == mMediaSizeSpinner) {
+ SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
+ if (mOrientationSpinner.getSelectedItemPosition() == 0) {
+ mPrintJob.getAttributes().setMediaSize(mediaItem.value.asPortrait());
+ } else {
+ mPrintJob.getAttributes().setMediaSize(mediaItem.value.asLandscape());
+ }
+ } else if (spinner == mColorModeSpinner) {
+ SpinnerItem<Integer> colorModeItem = mColorModeSpinnerAdapter.getItem(position);
+ mPrintJob.getAttributes().setColorMode(colorModeItem.value);
+ } else if (spinner == mOrientationSpinner) {
+ SpinnerItem<Integer> orientationItem = mOrientationSpinnerAdapter.getItem(position);
+ PrintAttributes attributes = mPrintJob.getAttributes();
+ if (mMediaSizeSpinner.getSelectedItem() != null) {
+ if (orientationItem.value == ORIENTATION_PORTRAIT) {
+ attributes.copyFrom(attributes.asPortrait());
+ } else {
+ attributes.copyFrom(attributes.asLandscape());
+ }
+ }
+ }
+
+ if (canUpdateDocument()) {
+ updateDocument(true, false);
+ }
+
+ updateOptionsUi();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ /* do nothing*/
+ }
+ }
+
+ private boolean canUpdateDocument() {
+ if (mPrintedDocument.isDestroyed()) {
+ return false;
+ }
+
+ if (hasErrors()) {
+ return false;
+ }
+
+ PrintAttributes attributes = mPrintJob.getAttributes();
+
+ final int colorMode = attributes.getColorMode();
+ if (colorMode != PrintAttributes.COLOR_MODE_COLOR
+ && colorMode != PrintAttributes.COLOR_MODE_MONOCHROME) {
+ return false;
+ }
+ if (attributes.getMediaSize() == null) {
+ return false;
+ }
+ if (attributes.getMinMargins() == null) {
+ return false;
+ }
+ if (attributes.getResolution() == null) {
+ return false;
+ }
+
+ PrinterInfo currentPrinter = getCurrentPrinter();
+ if (currentPrinter == null) {
+ return false;
+ }
+ PrinterCapabilitiesInfo capabilities = currentPrinter.getCapabilities();
+ if (capabilities == null) {
+ return false;
+ }
+ if (currentPrinter.getStatus() == PrinterInfo.STATUS_UNAVAILABLE) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private final class SelectAllOnFocusListener implements OnFocusChangeListener {
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ EditText editText = (EditText) view;
+ if (!TextUtils.isEmpty(editText.getText())) {
+ editText.setSelection(editText.getText().length());
+ }
+ }
+ }
+
+ private final class RangeTextWatcher implements TextWatcher {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ /* do nothing */
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ /* do nothing */
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ final boolean hadErrors = hasErrors();
+
+ String text = editable.toString();
+
+ if (TextUtils.isEmpty(text)) {
+ mPageRangeEditText.setError("");
+ updateOptionsUi();
+ return;
+ }
+
+ String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
+ if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
+ mPageRangeEditText.setError("");
+ updateOptionsUi();
+ return;
+ }
+
+ PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
+ final int pageCount = (info != null) ? info.getPageCount() : 0;
+
+ // The range
+ Matcher matcher = PATTERN_DIGITS.matcher(text);
+ while (matcher.find()) {
+ String numericString = text.substring(matcher.start(), matcher.end()).trim();
+ if (TextUtils.isEmpty(numericString)) {
+ continue;
+ }
+ final int pageIndex = Integer.parseInt(numericString);
+ if (pageIndex < 1 || pageIndex > pageCount) {
+ mPageRangeEditText.setError("");
+ updateOptionsUi();
+ return;
+ }
+ }
+
+ // We intentionally do not catch the case of the from page being
+ // greater than the to page. When computing the requested pages
+ // we just swap them if necessary.
+
+ // Keep the print job up to date with the selected pages if we
+ // know how many pages are there in the document.
+ mRequestedPages = computeRequestedPages();
+
+ mPageRangeEditText.setError(null);
+ mPrintButton.setEnabled(true);
+ updateOptionsUi();
+
+ if (hadErrors && !hasErrors()) {
+ updateOptionsUi();
+ }
+ }
+ }
+
+ private final class EditTextWatcher implements TextWatcher {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ /* do nothing */
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ /* do nothing */
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ final boolean hadErrors = hasErrors();
+
+ if (editable.length() == 0) {
+ mCopiesEditText.setError("");
+ updateOptionsUi();
+ return;
+ }
+
+ int copies = 0;
+ try {
+ copies = Integer.parseInt(editable.toString());
+ } catch (NumberFormatException nfe) {
+ /* ignore */
+ }
+
+ if (copies < MIN_COPIES) {
+ mCopiesEditText.setError("");
+ updateOptionsUi();
+ return;
+ }
+
+ mPrintJob.setCopies(copies);
+
+ mCopiesEditText.setError(null);
+
+ updateOptionsUi();
+
+ if (hadErrors && canUpdateDocument()) {
+ updateDocument(true, false);
+ }
+ }
+ }
+
+ private final class ProgressMessageController implements Runnable {
+ private static final long PROGRESS_TIMEOUT_MILLIS = 1000;
+
+ private final Handler mHandler;
+
+ private boolean mPosted;
+
+ public ProgressMessageController(Context context) {
+ mHandler = new Handler(context.getMainLooper(), null, false);
+ }
+
+ public void post() {
+ if (mPosted) {
+ return;
+ }
+ mPosted = true;
+ mHandler.postDelayed(this, PROGRESS_TIMEOUT_MILLIS);
+ }
+
+ public void cancel() {
+ if (!mPosted) {
+ return;
+ }
+ mPosted = false;
+ mHandler.removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ ensureProgressUiShown();
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintErrorFragment.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintErrorFragment.java
new file mode 100644
index 0000000..b708356
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintErrorFragment.java
@@ -0,0 +1,103 @@
+/*
+ * 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.printspooler.ui;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.printspooler.R;
+
+/**
+ * Fragment for showing an error UI.
+ */
+public final class PrintErrorFragment extends Fragment {
+ public static final int ACTION_NONE = 0;
+ public static final int ACTION_RETRY = 1;
+ public static final int ACTION_CONFIRM = 2;
+
+ public interface OnActionListener {
+ public void onActionPerformed();
+ }
+
+ private static final String EXTRA_ERROR_MESSAGE = "EXTRA_ERROR_MESSAGE";
+ private static final String EXTRA_ACTION = "EXTRA_ACTION";
+
+ public static PrintErrorFragment newInstance(CharSequence errorMessage, int action) {
+ PrintErrorFragment instance = new PrintErrorFragment();
+ Bundle arguments = new Bundle();
+ arguments.putCharSequence(EXTRA_ERROR_MESSAGE, errorMessage);
+ arguments.putInt(EXTRA_ACTION, action);
+ instance.setArguments(arguments);
+ return instance;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup root,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.print_error_fragment, root, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ Bundle arguments = getArguments();
+
+ CharSequence error = arguments.getString(EXTRA_ERROR_MESSAGE);
+ if (!TextUtils.isEmpty(error)) {
+ TextView message = (TextView) view.findViewById(R.id.message);
+ message.setText(error);
+ }
+
+ Button actionButton = (Button) view.findViewById(R.id.action_button);
+
+ final int action = getArguments().getInt(EXTRA_ACTION);
+ switch (action) {
+ case ACTION_RETRY: {
+ actionButton.setVisibility(View.VISIBLE);
+ actionButton.setText(R.string.print_error_retry);
+ } break;
+
+ case ACTION_CONFIRM: {
+ actionButton.setVisibility(View.VISIBLE);
+ actionButton.setText(android.R.string.ok);
+ } break;
+
+ case ACTION_NONE: {
+ actionButton.setVisibility(View.GONE);
+ } break;
+ }
+
+ actionButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Activity activity = getActivity();
+ if (activity instanceof OnActionListener) {
+ ((OnActionListener) getActivity()).onActionPerformed();
+ }
+ }
+ });
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewFragment.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewFragment.java
new file mode 100644
index 0000000..d68a6aa
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewFragment.java
@@ -0,0 +1,12 @@
+package com.android.printspooler.ui;
+
+import android.app.Fragment;
+
+public class PrintPreviewFragment extends Fragment {
+
+ public static PrintPreviewFragment newInstance() {
+ return new PrintPreviewFragment();
+ }
+
+ // TODO: Implement
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintProgressFragment.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintProgressFragment.java
new file mode 100644
index 0000000..96aa153d
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintProgressFragment.java
@@ -0,0 +1,69 @@
+/*
+ * 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.printspooler.ui;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import android.widget.TextView;
+import com.android.printspooler.R;
+
+/**
+ * Fragment for showing a work in progress UI.
+ */
+public final class PrintProgressFragment extends Fragment {
+
+ public interface OnCancelRequestListener {
+ public void onCancelRequest();
+ }
+
+ public static PrintProgressFragment newInstance() {
+ return new PrintProgressFragment();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup root,
+ Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.print_progress_fragment, root, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ final Button cancelButton = (Button) view.findViewById(R.id.cancel_button);
+ final TextView message = (TextView) view.findViewById(R.id.message);
+
+ cancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Activity activity = getActivity();
+ if (activity instanceof OnCancelRequestListener) {
+ ((OnCancelRequestListener) getActivity()).onCancelRequest();
+ }
+ cancelButton.setVisibility(View.GONE);
+ message.setVisibility(View.VISIBLE);
+ }
+ });
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
new file mode 100644
index 0000000..7816d66
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
@@ -0,0 +1,168 @@
+/*
+ * 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.printspooler.ui;
+
+import android.app.Activity;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Loader;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PrinterRegistry {
+
+ private static final int LOADER_ID_PRINTERS_LOADER = 1;
+
+ private final Activity mActivity;
+
+ private final List<PrinterInfo> mPrinters = new ArrayList<>();
+
+ private final Runnable mReadyCallback;
+
+ private final Handler mHandler;
+
+ private boolean mReady;
+
+ private OnPrintersChangeListener mOnPrintersChangeListener;
+
+ public interface OnPrintersChangeListener {
+ public void onPrintersChanged(List<PrinterInfo> printers);
+ public void onPrintersInvalid();
+ }
+
+ public PrinterRegistry(Activity activity, Runnable readyCallback) {
+ mActivity = activity;
+ mReadyCallback = readyCallback;
+ mHandler = new MyHandler(activity.getMainLooper());
+ activity.getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER,
+ null, mLoaderCallbacks);
+ }
+
+ public void setOnPrintersChangeListener(OnPrintersChangeListener listener) {
+ mOnPrintersChangeListener = listener;
+ }
+
+ public List<PrinterInfo> getPrinters() {
+ return mPrinters;
+ }
+
+ public void addHistoricalPrinter(PrinterInfo printer) {
+ getPrinterProvider().addHistoricalPrinter(printer);
+ }
+
+ public void forgetFavoritePrinter(PrinterId printerId) {
+ getPrinterProvider().forgetFavoritePrinter(printerId);
+ }
+
+ public boolean isFavoritePrinter(PrinterId printerId) {
+ return getPrinterProvider().isFavoritePrinter(printerId);
+ }
+
+ public void setTrackedPrinter(PrinterId printerId) {
+ getPrinterProvider().setTrackedPrinter(printerId);
+ }
+
+ private FusedPrintersProvider getPrinterProvider() {
+ Loader<?> loader = mActivity.getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
+ return (FusedPrintersProvider) loader;
+ }
+
+ private final LoaderCallbacks<List<PrinterInfo>> mLoaderCallbacks =
+ new LoaderCallbacks<List<PrinterInfo>>() {
+ @Override
+ public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
+ if (loader.getId() == LOADER_ID_PRINTERS_LOADER) {
+ mPrinters.clear();
+ if (mOnPrintersChangeListener != null) {
+ // Post a message as we are in onLoadFinished and certain operations
+ // are not allowed in this callback, such as fragment transactions.
+ // Clients should not handle this explicitly.
+ mHandler.obtainMessage(MyHandler.MSG_PRINTERS_INVALID,
+ mOnPrintersChangeListener).sendToTarget();
+ }
+ }
+ }
+
+ // LoaderCallbacks#onLoadFinished
+ @Override
+ public void onLoadFinished(Loader<List<PrinterInfo>> loader, List<PrinterInfo> printers) {
+ if (loader.getId() == LOADER_ID_PRINTERS_LOADER) {
+ mPrinters.clear();
+ mPrinters.addAll(printers);
+ if (mOnPrintersChangeListener != null) {
+ // Post a message as we are in onLoadFinished and certain operations
+ // are not allowed in this callback, such as fragment transactions.
+ // Clients should not handle this explicitly.
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = mOnPrintersChangeListener;
+ args.arg2 = printers;
+ mHandler.obtainMessage(MyHandler.MSG_PRINTERS_CHANGED, args).sendToTarget();
+ }
+ if (!mReady) {
+ mReady = true;
+ if (mReadyCallback != null) {
+ mReadyCallback.run();
+ }
+ }
+ }
+ }
+
+ // LoaderCallbacks#onCreateLoader
+ @Override
+ public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
+ if (id == LOADER_ID_PRINTERS_LOADER) {
+ return new FusedPrintersProvider(mActivity);
+ }
+ return null;
+ }
+ };
+
+ private static final class MyHandler extends Handler {
+ public static final int MSG_PRINTERS_CHANGED = 0;
+ public static final int MSG_PRINTERS_INVALID = 1;
+
+ public MyHandler(Looper looper) {
+ super(looper, null , false);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_PRINTERS_CHANGED: {
+ SomeArgs args = (SomeArgs) message.obj;
+ OnPrintersChangeListener callback = (OnPrintersChangeListener) args.arg1;
+ List<PrinterInfo> printers = (List<PrinterInfo>) args.arg2;
+ args.recycle();
+ callback.onPrintersChanged(printers);
+ } break;
+
+ case MSG_PRINTERS_INVALID: {
+ OnPrintersChangeListener callback = (OnPrintersChangeListener) message.obj;
+ callback.onPrintersInvalid();
+ } break;
+ }
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
new file mode 100644
index 0000000..7715579
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2013 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.printspooler.ui;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.SearchView;
+import android.widget.TextView;
+
+import com.android.printspooler.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is an activity for selecting a printer.
+ */
+public final class SelectPrinterActivity extends Activity {
+
+ private static final String LOG_TAG = "SelectPrinterFragment";
+
+ public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
+
+ private static final String FRAGMENT_TAG_ADD_PRINTER_DIALOG =
+ "FRAGMENT_TAG_ADD_PRINTER_DIALOG";
+
+ private static final String FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS =
+ "FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS";
+
+ private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
+
+ private final ArrayList<PrintServiceInfo> mAddPrinterServices =
+ new ArrayList<>();
+
+ private PrinterRegistry mPrinterRegistry;
+
+ private ListView mListView;
+
+ private AnnounceFilterResult mAnnounceFilterResult;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getActionBar().setIcon(R.drawable.ic_menu_print);
+
+ setContentView(R.layout.select_printer_activity);
+
+ mPrinterRegistry = new PrinterRegistry(this, null);
+
+ // Hook up the list view.
+ mListView = (ListView) findViewById(android.R.id.list);
+ final DestinationAdapter adapter = new DestinationAdapter();
+ adapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ if (!isFinishing() && adapter.getCount() <= 0) {
+ updateEmptyView(adapter);
+ }
+ }
+
+ @Override
+ public void onInvalidated() {
+ if (!isFinishing()) {
+ updateEmptyView(adapter);
+ }
+ }
+ });
+ mListView.setAdapter(adapter);
+
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (!((DestinationAdapter) mListView.getAdapter()).isActionable(position)) {
+ return;
+ }
+
+ PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
+ onPrinterSelected(printer.getId());
+ }
+ });
+
+ registerForContextMenu(mListView);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+
+ getMenuInflater().inflate(R.menu.select_printer_activity, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ SearchView searchView = (SearchView) searchItem.getActionView();
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String searchString) {
+ ((DestinationAdapter) mListView.getAdapter()).getFilter().filter(searchString);
+ return true;
+ }
+ });
+ searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ if (AccessibilityManager.getInstance(SelectPrinterActivity.this).isEnabled()) {
+ view.announceForAccessibility(getString(
+ R.string.print_search_box_shown_utterance));
+ }
+ }
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ if (!isFinishing() && AccessibilityManager.getInstance(
+ SelectPrinterActivity.this).isEnabled()) {
+ view.announceForAccessibility(getString(
+ R.string.print_search_box_hidden_utterance));
+ }
+ }
+ });
+
+ if (mAddPrinterServices.isEmpty()) {
+ menu.removeItem(R.id.action_add_printer);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+ if (view == mListView) {
+ final int position = ((AdapterContextMenuInfo) menuInfo).position;
+ PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
+
+ menu.setHeaderTitle(printer.getName());
+
+ // Add the select menu item if applicable.
+ if (printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE) {
+ MenuItem selectItem = menu.add(Menu.NONE, R.string.print_select_printer,
+ Menu.NONE, R.string.print_select_printer);
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
+ selectItem.setIntent(intent);
+ }
+
+ // Add the forget menu item if applicable.
+ if (mPrinterRegistry.isFavoritePrinter(printer.getId())) {
+ MenuItem forgetItem = menu.add(Menu.NONE, R.string.print_forget_printer,
+ Menu.NONE, R.string.print_forget_printer);
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_PRINTER_ID, printer.getId());
+ forgetItem.setIntent(intent);
+ }
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.string.print_select_printer: {
+ PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID);
+ onPrinterSelected(printerId);
+ } return true;
+
+ case R.string.print_forget_printer: {
+ PrinterId printerId = item.getIntent().getParcelableExtra(EXTRA_PRINTER_ID);
+ mPrinterRegistry.forgetFavoritePrinter(printerId);
+ } return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateServicesWithAddPrinterActivity();
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onPause() {
+ if (mAnnounceFilterResult != null) {
+ mAnnounceFilterResult.remove();
+ }
+ super.onPause();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.action_add_printer) {
+ showAddPrinterSelectionDialog();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void onPrinterSelected(PrinterId printerId) {
+ Intent intent = new Intent();
+ intent.putExtra(INTENT_EXTRA_PRINTER_ID, printerId);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ private void updateServicesWithAddPrinterActivity() {
+ mAddPrinterServices.clear();
+
+ // Get all enabled print services.
+ PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
+ List<PrintServiceInfo> enabledServices = printManager.getEnabledPrintServices();
+
+ // No enabled print services - done.
+ if (enabledServices.isEmpty()) {
+ return;
+ }
+
+ // Find the services with valid add printers activities.
+ final int enabledServiceCount = enabledServices.size();
+ for (int i = 0; i < enabledServiceCount; i++) {
+ PrintServiceInfo enabledService = enabledServices.get(i);
+
+ // No add printers activity declared - next.
+ if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) {
+ continue;
+ }
+
+ ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
+ ComponentName addPrintersComponentName = new ComponentName(
+ serviceInfo.packageName, enabledService.getAddPrintersActivityName());
+ Intent addPritnersIntent = new Intent()
+ .setComponent(addPrintersComponentName);
+
+ // The add printers activity is valid - add it.
+ PackageManager pm = getPackageManager();
+ List<ResolveInfo> resolvedActivities = pm.queryIntentActivities(addPritnersIntent, 0);
+ if (!resolvedActivities.isEmpty()) {
+ // The activity is a component name, therefore it is one or none.
+ ActivityInfo activityInfo = resolvedActivities.get(0).activityInfo;
+ if (activityInfo.exported
+ && (activityInfo.permission == null
+ || pm.checkPermission(activityInfo.permission, getPackageName())
+ == PackageManager.PERMISSION_GRANTED)) {
+ mAddPrinterServices.add(enabledService);
+ }
+ }
+ }
+ }
+
+ private void showAddPrinterSelectionDialog() {
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ Fragment oldFragment = getFragmentManager().findFragmentByTag(
+ FRAGMENT_TAG_ADD_PRINTER_DIALOG);
+ if (oldFragment != null) {
+ transaction.remove(oldFragment);
+ }
+ AddPrinterAlertDialogFragment newFragment = new AddPrinterAlertDialogFragment();
+ Bundle arguments = new Bundle();
+ arguments.putParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS,
+ mAddPrinterServices);
+ newFragment.setArguments(arguments);
+ transaction.add(newFragment, FRAGMENT_TAG_ADD_PRINTER_DIALOG);
+ transaction.commit();
+ }
+
+ public void updateEmptyView(DestinationAdapter adapter) {
+ if (mListView.getEmptyView() == null) {
+ View emptyView = findViewById(R.id.empty_print_state);
+ mListView.setEmptyView(emptyView);
+ }
+ TextView titleView = (TextView) findViewById(R.id.title);
+ View progressBar = findViewById(R.id.progress_bar);
+ if (adapter.getUnfilteredCount() <= 0) {
+ titleView.setText(R.string.print_searching_for_printers);
+ progressBar.setVisibility(View.VISIBLE);
+ } else {
+ titleView.setText(R.string.print_no_printers);
+ progressBar.setVisibility(View.GONE);
+ }
+ }
+
+ private void announceSearchResultIfNeeded() {
+ if (AccessibilityManager.getInstance(this).isEnabled()) {
+ if (mAnnounceFilterResult == null) {
+ mAnnounceFilterResult = new AnnounceFilterResult();
+ }
+ mAnnounceFilterResult.post();
+ }
+ }
+
+ public static class AddPrinterAlertDialogFragment extends DialogFragment {
+
+ private String mAddPrintServiceItem;
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.choose_print_service);
+
+ final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
+ getArguments().getParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS);
+
+ final ArrayAdapter<String> adapter = new ArrayAdapter<>(
+ getActivity(), android.R.layout.simple_list_item_1);
+ final int printServiceCount = printServices.size();
+ for (int i = 0; i < printServiceCount; i++) {
+ PrintServiceInfo printService = printServices.get(i);
+ adapter.add(printService.getResolveInfo().loadLabel(
+ getActivity().getPackageManager()).toString());
+ }
+
+ final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
+ Settings.Secure.PRINT_SERVICE_SEARCH_URI);
+ final Intent viewIntent;
+ if (!TextUtils.isEmpty(searchUri)) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
+ if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) {
+ viewIntent = intent;
+ mAddPrintServiceItem = getString(R.string.add_print_service_label);
+ adapter.add(mAddPrintServiceItem);
+ } else {
+ viewIntent = null;
+ }
+ } else {
+ viewIntent = null;
+ }
+
+ builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String item = adapter.getItem(which);
+ if (item.equals(mAddPrintServiceItem)) {
+ try {
+ startActivity(viewIntent);
+ } catch (ActivityNotFoundException anfe) {
+ Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
+ }
+ } else {
+ PrintServiceInfo printService = printServices.get(which);
+ ComponentName componentName = new ComponentName(
+ printService.getResolveInfo().serviceInfo.packageName,
+ printService.getAddPrintersActivityName());
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setComponent(componentName);
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException anfe) {
+ Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
+ }
+ }
+ }
+ });
+
+ return builder.create();
+ }
+ }
+
+ private final class DestinationAdapter extends BaseAdapter implements Filterable {
+
+ private final Object mLock = new Object();
+
+ private final List<PrinterInfo> mPrinters = new ArrayList<>();
+
+ private final List<PrinterInfo> mFilteredPrinters = new ArrayList<>();
+
+ private CharSequence mLastSearchString;
+
+ public DestinationAdapter() {
+ mPrinterRegistry.setOnPrintersChangeListener(new PrinterRegistry.OnPrintersChangeListener() {
+ @Override
+ public void onPrintersChanged(List<PrinterInfo> printers) {
+ synchronized (mLock) {
+ mPrinters.clear();
+ mPrinters.addAll(printers);
+ mFilteredPrinters.clear();
+ mFilteredPrinters.addAll(printers);
+ if (!TextUtils.isEmpty(mLastSearchString)) {
+ getFilter().filter(mLastSearchString);
+ }
+ }
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onPrintersInvalid() {
+ synchronized (mLock) {
+ mPrinters.clear();
+ mFilteredPrinters.clear();
+ }
+ notifyDataSetInvalidated();
+ }
+ });
+ }
+
+ @Override
+ public Filter getFilter() {
+ return new Filter() {
+ @Override
+ protected FilterResults performFiltering(CharSequence constraint) {
+ synchronized (mLock) {
+ if (TextUtils.isEmpty(constraint)) {
+ return null;
+ }
+ FilterResults results = new FilterResults();
+ List<PrinterInfo> filteredPrinters = new ArrayList<>();
+ String constraintLowerCase = constraint.toString().toLowerCase();
+ final int printerCount = mPrinters.size();
+ for (int i = 0; i < printerCount; i++) {
+ PrinterInfo printer = mPrinters.get(i);
+ if (printer.getName().toLowerCase().contains(constraintLowerCase)) {
+ filteredPrinters.add(printer);
+ }
+ }
+ results.values = filteredPrinters;
+ results.count = filteredPrinters.size();
+ return results;
+ }
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected void publishResults(CharSequence constraint, FilterResults results) {
+ final boolean resultCountChanged;
+ synchronized (mLock) {
+ final int oldPrinterCount = mFilteredPrinters.size();
+ mLastSearchString = constraint;
+ mFilteredPrinters.clear();
+ if (results == null) {
+ mFilteredPrinters.addAll(mPrinters);
+ } else {
+ List<PrinterInfo> printers = (List<PrinterInfo>) results.values;
+ mFilteredPrinters.addAll(printers);
+ }
+ resultCountChanged = (oldPrinterCount != mFilteredPrinters.size());
+ }
+ if (resultCountChanged) {
+ announceSearchResultIfNeeded();
+ }
+ notifyDataSetChanged();
+ }
+ };
+ }
+
+ public int getUnfilteredCount() {
+ synchronized (mLock) {
+ return mPrinters.size();
+ }
+ }
+
+ @Override
+ public int getCount() {
+ synchronized (mLock) {
+ return mFilteredPrinters.size();
+ }
+ }
+
+ @Override
+ public Object getItem(int position) {
+ synchronized (mLock) {
+ return mFilteredPrinters.get(position);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ return getView(position, convertView, parent);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(
+ R.layout.printer_list_item, parent, false);
+ }
+
+ convertView.setEnabled(isActionable(position));
+
+ PrinterInfo printer = (PrinterInfo) getItem(position);
+
+ CharSequence title = printer.getName();
+ CharSequence subtitle = null;
+ Drawable icon = null;
+
+ try {
+ PackageManager pm = getPackageManager();
+ PackageInfo packageInfo = pm.getPackageInfo(printer.getId()
+ .getServiceName().getPackageName(), 0);
+ subtitle = packageInfo.applicationInfo.loadLabel(pm);
+ icon = packageInfo.applicationInfo.loadIcon(pm);
+ } catch (NameNotFoundException nnfe) {
+ /* ignore */
+ }
+
+ TextView titleView = (TextView) convertView.findViewById(R.id.title);
+ titleView.setText(title);
+
+ TextView subtitleView = (TextView) convertView.findViewById(R.id.subtitle);
+ if (!TextUtils.isEmpty(subtitle)) {
+ subtitleView.setText(subtitle);
+ subtitleView.setVisibility(View.VISIBLE);
+ } else {
+ subtitleView.setText(null);
+ subtitleView.setVisibility(View.GONE);
+ }
+
+
+ ImageView iconView = (ImageView) convertView.findViewById(R.id.icon);
+ if (icon != null) {
+ iconView.setImageDrawable(icon);
+ iconView.setVisibility(View.VISIBLE);
+ } else {
+ iconView.setVisibility(View.GONE);
+ }
+
+ return convertView;
+ }
+
+ public boolean isActionable(int position) {
+ PrinterInfo printer = (PrinterInfo) getItem(position);
+ return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+ }
+ }
+
+ private final class AnnounceFilterResult implements Runnable {
+ private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec
+
+ public void post() {
+ remove();
+ mListView.postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
+ }
+
+ public void remove() {
+ mListView.removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ final int count = mListView.getAdapter().getCount();
+ final String text;
+ if (count <= 0) {
+ text = getString(R.string.print_no_printers);
+ } else {
+ text = getResources().getQuantityString(
+ R.plurals.print_search_result_count_utterance, count, count);
+ }
+ mListView.announceForAccessibility(text);
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/MediaSizeUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/MediaSizeUtils.java
new file mode 100644
index 0000000..912ee1d
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/MediaSizeUtils.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013 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.printspooler.util;
+
+import android.content.Context;
+import android.print.PrintAttributes.MediaSize;
+import android.util.ArrayMap;
+
+import com.android.printspooler.R;
+
+import java.util.Comparator;
+import java.util.Map;
+
+/**
+ * Utility functions and classes for dealing with media sizes.
+ */
+public final class MediaSizeUtils {
+
+ private static Map<MediaSize, String> sMediaSizeToStandardMap;
+
+ private MediaSizeUtils() {
+ /* do nothing - hide constructor */
+ }
+
+ /**
+ * Gets the default media size for the current locale.
+ *
+ * @param context Context for accessing resources.
+ * @return The default media size.
+ */
+ public static MediaSize getDefault(Context context) {
+ String mediaSizeId = context.getString(R.string.mediasize_default);
+ return MediaSize.getStandardMediaSizeById(mediaSizeId);
+ }
+
+ private static String getStandardForMediaSize(Context context, MediaSize mediaSize) {
+ if (sMediaSizeToStandardMap == null) {
+ sMediaSizeToStandardMap = new ArrayMap<MediaSize, String>();
+ String[] mediaSizeToStandardMapValues = context.getResources()
+ .getStringArray(R.array.mediasize_to_standard_map);
+ final int mediaSizeToStandardCount = mediaSizeToStandardMapValues.length;
+ for (int i = 0; i < mediaSizeToStandardCount; i += 2) {
+ String mediaSizeId = mediaSizeToStandardMapValues[i];
+ MediaSize key = MediaSize.getStandardMediaSizeById(mediaSizeId);
+ String value = mediaSizeToStandardMapValues[i + 1];
+ sMediaSizeToStandardMap.put(key, value);
+ }
+ }
+ String standard = sMediaSizeToStandardMap.get(mediaSize);
+ return (standard != null) ? standard : context.getString(
+ R.string.mediasize_standard_iso);
+ }
+
+ /**
+ * Comparator for ordering standard media sizes. The ones for the current
+ * standard go to the top and the ones for the other standards follow grouped
+ * by standard. Media sizes of the same standard are ordered alphabetically.
+ */
+ public static final class MediaSizeComparator implements Comparator<MediaSize> {
+ private final Context mContext;
+
+ public MediaSizeComparator(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public int compare(MediaSize lhs, MediaSize rhs) {
+ String currentStandard = mContext.getString(R.string.mediasize_standard);
+ String lhsStandard = getStandardForMediaSize(mContext, lhs);
+ String rhsStandard = getStandardForMediaSize(mContext, rhs);
+
+ // The current standard always wins.
+ if (lhsStandard.equals(currentStandard)) {
+ if (!rhsStandard.equals(currentStandard)) {
+ return -1;
+ }
+ } else if (rhsStandard.equals(currentStandard)) {
+ return 1;
+ }
+
+ if (!lhsStandard.equals(rhsStandard)) {
+ // Different standards - use the standard ordering.
+ return lhsStandard.compareTo(rhsStandard);
+ } else {
+ // Same standard - sort alphabetically by label.
+ return lhs.getLabel(mContext.getPackageManager()).
+ compareTo(rhs.getLabel(mContext.getPackageManager()));
+ }
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java
new file mode 100644
index 0000000..33b294f
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler.util;
+
+import android.print.PageRange;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * This class contains utility functions for working with page ranges.
+ */
+public final class PageRangeUtils {
+
+ private static final PageRange[] ALL_PAGES_RANGE = new PageRange[] {PageRange.ALL_PAGES};
+
+ private static final Comparator<PageRange> sComparator = new Comparator<PageRange>() {
+ @Override
+ public int compare(PageRange lhs, PageRange rhs) {
+ return lhs.getStart() - rhs.getStart();
+ }
+ };
+
+ private PageRangeUtils() {
+ /* do nothing - hide constructor */
+ }
+
+ /**
+ * Checks whether one page range array contains another one.
+ *
+ * @param ourRanges The container page ranges.
+ * @param otherRanges The contained page ranges.
+ * @return Whether the container page ranges contains the contained ones.
+ */
+ public static boolean contains(PageRange[] ourRanges, PageRange[] otherRanges) {
+ if (ourRanges == null || otherRanges == null) {
+ return false;
+ }
+
+ if (Arrays.equals(ourRanges, ALL_PAGES_RANGE)) {
+ return true;
+ }
+
+ ourRanges = normalize(ourRanges);
+ otherRanges = normalize(otherRanges);
+
+ // Note that the code below relies on the ranges being normalized
+ // which is they contain monotonically increasing non-intersecting
+ // sub-ranges whose start is less that or equal to the end.
+ int otherRangeIdx = 0;
+ final int ourRangeCount = ourRanges.length;
+ final int otherRangeCount = otherRanges.length;
+ for (int ourRangeIdx = 0; ourRangeIdx < ourRangeCount; ourRangeIdx++) {
+ PageRange ourRange = ourRanges[ourRangeIdx];
+ for (; otherRangeIdx < otherRangeCount; otherRangeIdx++) {
+ PageRange otherRange = otherRanges[otherRangeIdx];
+ if (otherRange.getStart() > ourRange.getEnd()) {
+ break;
+ }
+ if (otherRange.getStart() < ourRange.getStart()
+ || otherRange.getEnd() > ourRange.getEnd()) {
+ return false;
+ }
+ }
+ }
+ if (otherRangeIdx < otherRangeCount) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Normalizes a page range, which is the resulting page ranges are
+ * non-overlapping with the start lesser than or equal to the end
+ * and ordered in an ascending order.
+ *
+ * @param pageRanges The page ranges to normalize.
+ * @return The normalized page ranges.
+ */
+ public static PageRange[] normalize(PageRange[] pageRanges) {
+ if (pageRanges == null) {
+ return null;
+ }
+ final int oldRangeCount = pageRanges.length;
+ if (oldRangeCount <= 1) {
+ return pageRanges;
+ }
+ Arrays.sort(pageRanges, sComparator);
+ int newRangeCount = 1;
+ for (int i = 0; i < oldRangeCount - 1; i++) {
+ newRangeCount++;
+ PageRange currentRange = pageRanges[i];
+ PageRange nextRange = pageRanges[i + 1];
+ if (currentRange.getEnd() + 1 >= nextRange.getStart()) {
+ newRangeCount--;
+ pageRanges[i] = null;
+ pageRanges[i + 1] = new PageRange(currentRange.getStart(),
+ Math.max(currentRange.getEnd(), nextRange.getEnd()));
+ }
+ }
+ if (newRangeCount == oldRangeCount) {
+ return pageRanges;
+ }
+ return Arrays.copyOfRange(pageRanges, oldRangeCount - newRangeCount,
+ oldRangeCount);
+ }
+
+ /**
+ * Offsets a the start and end of page ranges with the given value.
+ *
+ * @param pageRanges The page ranges to offset.
+ * @param offset The offset value.
+ */
+ public static void offset(PageRange[] pageRanges, int offset) {
+ if (offset == 0) {
+ return;
+ }
+ final int pageRangeCount = pageRanges.length;
+ for (int i = 0; i < pageRangeCount; i++) {
+ final int start = pageRanges[i].getStart() + offset;
+ final int end = pageRanges[i].getEnd() + offset;
+ pageRanges[i] = new PageRange(start, end);
+ }
+ }
+
+ /**
+ * Gets the number of pages in a normalized range array.
+ *
+ * @param pageRanges Normalized page ranges.
+ * @param layoutPageCount Page count after reported after layout pass.
+ * @return The page count in the ranges.
+ */
+ public static int getNormalizedPageCount(PageRange[] pageRanges, int layoutPageCount) {
+ int pageCount = 0;
+ final int pageRangeCount = pageRanges.length;
+ for (int i = 0; i < pageRangeCount; i++) {
+ PageRange pageRange = pageRanges[i];
+ if (PageRange.ALL_PAGES.equals(pageRange)) {
+ return layoutPageCount;
+ }
+ pageCount += pageRange.getEnd() - pageRange.getStart() + 1;
+ }
+ return pageCount;
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java
new file mode 100644
index 0000000..446952d
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.printspooler.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ServiceInfo;
+import android.print.PrintManager;
+import android.printservice.PrintServiceInfo;
+
+import java.util.List;
+
+public class PrintOptionUtils {
+
+ private PrintOptionUtils() {
+ /* ignore - hide constructor */
+ }
+
+ /**
+ * Gets the advanced options activity name for a print service.
+ *
+ * @param context Context for accessing system resources.
+ * @param serviceName The print service name.
+ * @return The advanced options activity name or null.
+ */
+ public static String getAdvancedOptionsActivityName(Context context,
+ ComponentName serviceName) {
+ PrintManager printManager = (PrintManager) context.getSystemService(
+ Context.PRINT_SERVICE);
+ List<PrintServiceInfo> printServices = printManager.getEnabledPrintServices();
+ final int printServiceCount = printServices.size();
+ for (int i = 0; i < printServiceCount; i ++) {
+ PrintServiceInfo printServiceInfo = printServices.get(i);
+ ServiceInfo serviceInfo = printServiceInfo.getResolveInfo().serviceInfo;
+ if (serviceInfo.name.equals(serviceName.getClassName())
+ && serviceInfo.packageName.equals(serviceName.getPackageName())) {
+ return printServiceInfo.getAdvancedOptionsActivityName();
+ }
+ }
+ return null;
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/ContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/ContentView.java
new file mode 100644
index 0000000..77ca541
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/ContentView.java
@@ -0,0 +1,338 @@
+/*
+ * 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.printspooler.widget;
+
+import android.content.Context;
+import android.support.v4.widget.ViewDragHelper;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.printspooler.R;
+
+/**
+ * This class is a layout manager for the print screen. It has a sliding
+ * area that contains the print options. If the sliding area is open the
+ * print options are visible and if it is closed a summary of the print
+ * job is shown. Under the sliding area there is a place for putting
+ * arbitrary content such as preview, error message, progress indicator,
+ * etc. The sliding area is covering the content holder under it when
+ * the former is opened.
+ */
+@SuppressWarnings("unused")
+public final class ContentView extends ViewGroup implements View.OnClickListener {
+ private static final int FIRST_POINTER_ID = 0;
+
+ private final ViewDragHelper mDragger;
+
+ private View mStaticContent;
+ private ViewGroup mSummaryContent;
+ private View mDynamicContent;
+
+ private View mDraggableContent;
+ private ViewGroup mMoreOptionsContainer;
+ private ViewGroup mOptionsContainer;
+
+ private View mEmbeddedContentContainer;
+
+ private View mExpandCollapseHandle;
+ private View mExpandCollapseIcon;
+
+ private int mClosedOptionsOffsetY;
+ private int mCurrentOptionsOffsetY;
+
+ private OptionsStateChangeListener mOptionsStateChangeListener;
+
+ private int mOldDraggableHeight;
+
+ public interface OptionsStateChangeListener {
+ public void onOptionsOpened();
+ public void onOptionsClosed();
+ }
+
+ public ContentView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mDragger = ViewDragHelper.create(this, new DragCallbacks());
+
+ // The options view is sliding under the static header but appears
+ // after it in the layout, so we will draw in opposite order.
+ setChildrenDrawingOrderEnabled(true);
+ }
+
+ public void setOptionsStateChangeListener(OptionsStateChangeListener listener) {
+ mOptionsStateChangeListener = listener;
+ }
+
+ private boolean isOptionsOpened() {
+ return mCurrentOptionsOffsetY == 0;
+ }
+
+ private boolean isOptionsClosed() {
+ return mCurrentOptionsOffsetY == mClosedOptionsOffsetY;
+ }
+
+ private void openOptions() {
+ if (isOptionsOpened()) {
+ return;
+ }
+ mDragger.smoothSlideViewTo(mDynamicContent, mDynamicContent.getLeft(),
+ getOpenedOptionsY());
+ invalidate();
+ }
+
+ private void closeOptions() {
+ if (isOptionsClosed()) {
+ return;
+ }
+ mDragger.smoothSlideViewTo(mDynamicContent, mDynamicContent.getLeft(),
+ getClosedOptionsY());
+ invalidate();
+ }
+
+ @Override
+ protected int getChildDrawingOrder(int childCount, int i) {
+ return childCount - i - 1;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mStaticContent = findViewById(R.id.static_content);
+ mSummaryContent = (ViewGroup) findViewById(R.id.summary_content);
+ mDynamicContent = findViewById(R.id.dynamic_content);
+ mDraggableContent = findViewById(R.id.draggable_content);
+ mMoreOptionsContainer = (ViewGroup) findViewById(R.id.more_options_container);
+ mOptionsContainer = (ViewGroup) findViewById(R.id.options_container);
+ mEmbeddedContentContainer = findViewById(R.id.embedded_content_container);
+ mExpandCollapseIcon = findViewById(R.id.expand_collapse_icon);
+ mExpandCollapseHandle = findViewById(R.id.expand_collapse_handle);
+
+ mExpandCollapseIcon.setOnClickListener(this);
+ mExpandCollapseHandle.setOnClickListener(this);
+
+ // Make sure we start in a closed options state.
+ onDragProgress(1.0f);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view == mExpandCollapseHandle || view == mExpandCollapseIcon) {
+ if (isOptionsClosed()) {
+ openOptions();
+ } else if (isOptionsOpened()) {
+ closeOptions();
+ } // else in open/close progress do nothing.
+ }
+ }
+
+ @Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ /* do nothing */
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ mDragger.processTouchEvent(event);
+ return true;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return mDragger.shouldInterceptTouchEvent(event)
+ || super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public void computeScroll() {
+ if (mDragger.continueSettling(true)) {
+ postInvalidateOnAnimation();
+ }
+ }
+
+ private int getOpenedOptionsY() {
+ return mStaticContent.getBottom();
+ }
+
+ private int getClosedOptionsY() {
+ return getOpenedOptionsY() + mClosedOptionsOffsetY;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChild(mStaticContent, widthMeasureSpec, heightMeasureSpec);
+
+ if (mSummaryContent.getVisibility() != View.GONE) {
+ measureChild(mSummaryContent, widthMeasureSpec, heightMeasureSpec);
+ }
+
+ measureChild(mDynamicContent, widthMeasureSpec, heightMeasureSpec);
+
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+// // The height of the draggable content may change and if that happens
+// // we have to adjust the current offset to ensure the sliding area is
+// // at the same position.
+// mCurrentOptionsOffsetY -= mDraggableContent.getMeasuredHeight()
+// - oldDraggableHeight;
+
+ if (mOldDraggableHeight != mDraggableContent.getMeasuredHeight()) {
+ mCurrentOptionsOffsetY -= mDraggableContent.getMeasuredHeight()
+ - mOldDraggableHeight;
+ mOldDraggableHeight = mDraggableContent.getMeasuredHeight();
+ }
+
+ // The height of the draggable content may change and if that happens
+ // we have to adjust the sliding area closed state offset.
+ mClosedOptionsOffsetY = mSummaryContent.getMeasuredHeight()
+ - mDraggableContent.getMeasuredHeight();
+
+ // The content host must be maximally large size that fits entirely
+ // on the screen when the options are collapsed.
+ ViewGroup.LayoutParams params = mEmbeddedContentContainer.getLayoutParams();
+ if (params.height == 0) {
+ params.height = heightSize - mStaticContent.getMeasuredHeight()
+ - mSummaryContent.getMeasuredHeight() - mDynamicContent.getMeasuredHeight()
+ + mDraggableContent.getMeasuredHeight();
+
+ mCurrentOptionsOffsetY = mClosedOptionsOffsetY;
+ }
+
+ // The content host can grow vertically as much as needed - we will be covering it.
+ final int hostHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
+ measureChild(mEmbeddedContentContainer, widthMeasureSpec, hostHeightMeasureSpec);
+
+ setMeasuredDimension(resolveSize(MeasureSpec.getSize(widthMeasureSpec), widthMeasureSpec),
+ resolveSize(heightSize, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mStaticContent.layout(left, top, right, mStaticContent.getMeasuredHeight());
+
+ if (mSummaryContent.getVisibility() != View.GONE) {
+ mSummaryContent.layout(left, mStaticContent.getMeasuredHeight(), right,
+ mStaticContent.getMeasuredHeight() + mSummaryContent.getMeasuredHeight());
+ }
+
+ final int dynContentTop = mStaticContent.getMeasuredHeight() + mCurrentOptionsOffsetY;
+ final int dynContentBottom = dynContentTop + mDynamicContent.getMeasuredHeight();
+
+ mDynamicContent.layout(left, dynContentTop, right, dynContentBottom);
+
+ final int embContentTop = mStaticContent.getMeasuredHeight() + mClosedOptionsOffsetY
+ + mDynamicContent.getMeasuredHeight();
+ final int embContentBottom = embContentTop + mEmbeddedContentContainer.getMeasuredHeight();
+
+ mEmbeddedContentContainer.layout(left, embContentTop, right, embContentBottom);
+ }
+
+ private void onDragProgress(float progress) {
+ final int summaryCount = mSummaryContent.getChildCount();
+ for (int i = 0; i < summaryCount; i++) {
+ View child = mSummaryContent.getChildAt(i);
+ child.setAlpha(progress);
+ }
+
+ if (progress == 0) {
+ if (mOptionsStateChangeListener != null) {
+ mOptionsStateChangeListener.onOptionsOpened();
+ }
+ mSummaryContent.setVisibility(View.GONE);
+ mExpandCollapseIcon.setBackgroundResource(R.drawable.ic_expand_less);
+ } else {
+ mSummaryContent.setVisibility(View.VISIBLE);
+ }
+
+ final float inverseAlpha = 1.0f - progress;
+
+ final int optionCount = mOptionsContainer.getChildCount();
+ for (int i = 0; i < optionCount; i++) {
+ View child = mOptionsContainer.getChildAt(i);
+ child.setAlpha(inverseAlpha);
+ }
+
+ if (mMoreOptionsContainer.getVisibility() != View.GONE) {
+ final int moreOptionCount = mMoreOptionsContainer.getChildCount();
+ for (int i = 0; i < moreOptionCount; i++) {
+ View child = mMoreOptionsContainer.getChildAt(i);
+ child.setAlpha(inverseAlpha);
+ }
+ }
+
+ if (inverseAlpha == 0) {
+ if (mOptionsStateChangeListener != null) {
+ mOptionsStateChangeListener.onOptionsClosed();
+ }
+ if (mMoreOptionsContainer.getVisibility() != View.GONE) {
+ mMoreOptionsContainer.setVisibility(View.INVISIBLE);
+ }
+ mDraggableContent.setVisibility(View.INVISIBLE);
+ mExpandCollapseIcon.setBackgroundResource(R.drawable.ic_expand_more);
+ } else {
+ if (mMoreOptionsContainer.getVisibility() != View.GONE) {
+ mMoreOptionsContainer.setVisibility(View.VISIBLE);
+ }
+ mDraggableContent.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private final class DragCallbacks extends ViewDragHelper.Callback {
+ @Override
+ public boolean tryCaptureView(View child, int pointerId) {
+ return child == mDynamicContent && pointerId == FIRST_POINTER_ID;
+ }
+
+ @Override
+ public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+ mCurrentOptionsOffsetY += dy;
+ final float progress = ((float) top - getOpenedOptionsY())
+ / (getClosedOptionsY() - getOpenedOptionsY());
+
+ mDraggableContent.notifySubtreeAccessibilityStateChangedIfNeeded();
+
+ onDragProgress(progress);
+ }
+
+ public void onViewReleased(View child, float velocityX, float velocityY) {
+ final int childTop = child.getTop();
+
+ final int openedOptionsY = getOpenedOptionsY();
+ final int closedOptionsY = getClosedOptionsY();
+
+ if (childTop == openedOptionsY || childTop == closedOptionsY) {
+ return;
+ }
+
+ final int halfRange = closedOptionsY + (openedOptionsY - closedOptionsY) / 2;
+ if (childTop < halfRange) {
+ mDragger.smoothSlideViewTo(child, child.getLeft(), closedOptionsY);
+ } else {
+ mDragger.smoothSlideViewTo(child, child.getLeft(), openedOptionsY);
+ }
+
+ invalidate();
+ }
+
+ public int getViewVerticalDragRange(View child) {
+ return mDraggableContent.getHeight();
+ }
+
+ public int clampViewPositionVertical(View child, int top, int dy) {
+ final int staticOptionBottom = mStaticContent.getBottom();
+ return Math.max(Math.min(top, getOpenedOptionsY()), getClosedOptionsY());
+ }
+ }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java b/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
new file mode 100644
index 0000000..d6bb7c8
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/FirstFocusableEditText.java
@@ -0,0 +1,69 @@
+/*
+ * 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.printspooler.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.EditText;
+
+/**
+ * An instance of this class class is intended to be the first focusable
+ * in a layout to which the system automatically gives focus. It performs
+ * some voodoo to avoid the first tap on it to start an edit mode, rather
+ * to bring up the IME, i.e. to get the behavior as if the view was not
+ * focused.
+ */
+public final class FirstFocusableEditText extends EditText {
+ private boolean mClickedBeforeFocus;
+ private CharSequence mError;
+
+ public FirstFocusableEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public boolean performClick() {
+ super.performClick();
+ if (isFocused() && !mClickedBeforeFocus) {
+ clearFocus();
+ requestFocus();
+ }
+ mClickedBeforeFocus = true;
+ return true;
+ }
+
+ @Override
+ public CharSequence getError() {
+ return mError;
+ }
+
+ @Override
+ public void setError(CharSequence error, Drawable icon) {
+ setCompoundDrawables(null, null, icon, null);
+ mError = error;
+ }
+
+ protected void onFocusChanged(boolean gainFocus, int direction,
+ Rect previouslyFocusedRect) {
+ if (!gainFocus) {
+ mClickedBeforeFocus = false;
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+}
\ No newline at end of file
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java
new file mode 100644
index 0000000..23c8d08
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintOptionsLayout.java
@@ -0,0 +1,166 @@
+/*
+ * 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.printspooler.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.printspooler.R;
+
+/**
+ * This class is a layout manager for the print options. The options are
+ * arranged in a configurable number of columns and enough rows to fit all
+ * the options given the column count.
+ */
+@SuppressWarnings("unused")
+public final class PrintOptionsLayout extends ViewGroup {
+
+ private final int mColumnCount;
+
+ public PrintOptionsLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray typedArray = context.obtainStyledAttributes(attrs,
+ R.styleable.PrintOptionsLayout);
+ mColumnCount = typedArray.getInteger(R.styleable.PrintOptionsLayout_columnCount, 0);
+ typedArray.recycle();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+
+ final int columnWidth = (widthSize != 0)
+ ? (widthSize - mPaddingLeft - mPaddingRight) / mColumnCount : 0;
+
+ int width = 0;
+ int height = 0;
+ int childState = 0;
+
+ final int childCount = getChildCount();
+ final int rowCount = childCount / mColumnCount + childCount % mColumnCount;
+
+ for (int row = 0; row < rowCount; row++) {
+ int rowWidth = 0;
+ int rowHeight = 0;
+
+ for (int col = 0; col < mColumnCount; col++) {
+ final int childIndex = row * mColumnCount + col;
+
+ if (childIndex >= childCount) {
+ break;
+ }
+
+ View child = getChildAt(childIndex);
+
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+
+ MarginLayoutParams childParams = (MarginLayoutParams) child.getLayoutParams();
+
+ final int childWidthMeasureSpec;
+ if (columnWidth > 0) {
+ childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ columnWidth - childParams.getMarginStart() - childParams.getMarginEnd(),
+ MeasureSpec.EXACTLY);
+ } else {
+ childWidthMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
+ getPaddingStart() + getPaddingEnd() + width, childParams.width);
+ }
+
+ final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
+ getPaddingTop() + getPaddingBottom() + height, childParams.height);
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+
+ childState = combineMeasuredStates(childState, child.getMeasuredState());
+
+ rowWidth += child.getMeasuredWidth() + childParams.getMarginStart()
+ + childParams.getMarginEnd();
+
+ rowHeight = Math.max(rowHeight, child.getMeasuredHeight() + childParams.topMargin
+ + childParams.bottomMargin);
+ }
+
+ width = Math.max(width, rowWidth);
+ height += rowHeight;
+ }
+
+ width += getPaddingStart() + getPaddingEnd();
+ width = Math.max(width, getMinimumWidth());
+
+ height += getPaddingTop() + getPaddingBottom();
+ height = Math.max(height, getMinimumHeight());
+
+ setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
+ resolveSizeAndState(height, heightMeasureSpec,
+ childState << MEASURED_HEIGHT_STATE_SHIFT));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int childCount = getChildCount();
+ final int rowCount = childCount / mColumnCount + childCount % mColumnCount;
+
+ int cellStart = getPaddingStart();
+ int cellTop = getPaddingTop();
+
+ for (int row = 0; row < rowCount; row++) {
+ int rowHeight = 0;
+
+ for (int col = 0; col < mColumnCount; col++) {
+ final int childIndex = row * mColumnCount + col;
+
+ if (childIndex >= childCount) {
+ break;
+ }
+
+ View child = getChildAt(childIndex);
+
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+
+ MarginLayoutParams childParams = (MarginLayoutParams) child.getLayoutParams();
+
+ final int childLeft = cellStart + childParams.getMarginStart();
+ final int childTop = cellTop + childParams.topMargin;
+ final int childRight = childLeft + child.getMeasuredWidth();
+ final int childBottom = childTop + child.getMeasuredHeight();
+
+ child.layout(childLeft, childTop, childRight, childBottom);
+
+ cellStart = childRight + childParams.getMarginEnd();
+
+ rowHeight = Math.max(rowHeight, child.getMeasuredHeight()
+ + childParams.topMargin + childParams.bottomMargin);
+ }
+
+ cellStart = getPaddingStart();
+ cellTop += cellTop + rowHeight;
+ }
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new ViewGroup.MarginLayoutParams(getContext(), attrs);
+ }
+}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 0e025a9..a92ab7e 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -195,4 +195,7 @@
<!-- Default for Settings.Secure.WAKE_GESTURE_ENABLED -->
<bool name="def_wake_gesture_enabled">true</bool>
+ <!-- Default for Settings.Global.GUEST_USER_ENABLED -->
+ <bool name="def_guest_user_enabled">true</bool>
+
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index c4a54b7..09e6a94 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -70,7 +70,7 @@
// database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
// is properly propagated through your change. Not doing so will result in a loss of user
// settings.
- private static final int DATABASE_VERSION = 104;
+ private static final int DATABASE_VERSION = 105;
private Context mContext;
private int mUserHandle;
@@ -1677,6 +1677,24 @@
upgradeVersion = 104;
}
+ if (upgradeVersion < 105) {
+ if (mUserHandle == UserHandle.USER_OWNER) {
+ db.beginTransaction();
+ SQLiteStatement stmt = null;
+ try {
+ stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
+ + " VALUES(?,?);");
+ loadBooleanSetting(stmt, Settings.Global.GUEST_USER_ENABLED,
+ R.bool.def_guest_user_enabled);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ if (stmt != null) stmt.close();
+ }
+ }
+ upgradeVersion = 105;
+ }
+
// *** Remember to update DATABASE_VERSION above!
if (upgradeVersion != currentVersion) {
@@ -2410,6 +2428,8 @@
loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName());
+ loadBooleanSetting(stmt, Settings.Global.GUEST_USER_ENABLED,
+ R.bool.def_guest_user_enabled);
// --- New global settings start here
} finally {
if (stmt != null) stmt.close();
@@ -2468,7 +2488,7 @@
}
private String getDefaultDeviceName() {
- return mContext.getResources().getString(R.string.def_device_name, Build.BRAND,
+ return mContext.getResources().getString(R.string.def_device_name, Build.MANUFACTURER,
Build.MODEL);
}
}
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
index 99522b1..34481ba 100644
--- a/packages/Shell/res/values-de/strings.xml
+++ b/packages/Shell/res/values-de/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_finished_title" msgid="2293711546892863898">"Fehlerbericht erfasst"</string>
- <string name="bugreport_finished_text" msgid="3559904746859400732">"Berühren, um Fehlerbericht zu teilen"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Tippen, um Fehlerbericht zu teilen"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Fehlerberichte enthalten Daten aus verschiedenen Protokolldateien des Systems, darunter auch personenbezogene und private Daten. Teilen Sie Fehlerberichte nur mit Apps und Personen, denen Sie vertrauen."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Diese Nachricht nächstes Mal zeigen"</string>
</resources>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
index 1da6f1f..12f5e88 100644
--- a/packages/Shell/res/values-fr/strings.xml
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"Shell"</string>
<string name="bugreport_finished_title" msgid="2293711546892863898">"Rapport de bug enregistré"</string>
- <string name="bugreport_finished_text" msgid="3559904746859400732">"Appuyer ici pour partager votre rapport de bug"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"Appuyez ici pour partager le rapport de bug"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bug contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bug qu\'avec les applications et les personnes que vous estimez fiables."</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
</resources>
diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml
index 88b9c14..db34041 100644
--- a/packages/Shell/res/values-ja/strings.xml
+++ b/packages/Shell/res/values-ja/strings.xml
@@ -18,7 +18,7 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="3701846017049540910">"シェル"</string>
<string name="bugreport_finished_title" msgid="2293711546892863898">"バグレポートが記録されました"</string>
- <string name="bugreport_finished_text" msgid="3559904746859400732">"タップしてバグレポートを共有する"</string>
+ <string name="bugreport_finished_text" msgid="3559904746859400732">"バグレポートを共有するにはタップします"</string>
<string name="bugreport_confirm" msgid="5130698467795669780">"バグレポートには、個人の非公開情報など、システムのさまざまなログファイルのデータが含まれます。共有する場合は信頼するアプリとユーザーのみを選択してください。"</string>
<string name="bugreport_confirm_repeat" msgid="4926842460688645058">"このメッセージを次回も表示する"</string>
</resources>
diff --git a/packages/SystemUI/res/anim/recents_from_app_enter.xml b/packages/SystemUI/res/anim/recents_from_app_enter.xml
new file mode 100644
index 0000000..6abe8b3
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_app_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top">
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="0"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_from_app_exit.xml b/packages/SystemUI/res/anim/recents_from_app_exit.xml
new file mode 100644
index 0000000..1447a5a
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_app_exit.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="normal">
+
+ <!-- Animate the view out only after recents is visible -->
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="1"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
index 4bd7e82..bac8cb6 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_enter.xml
@@ -20,9 +20,9 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:zAdjustment="top">
- <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/accelerate_cubic"
- android:duration="250"/>
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="100"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
index becc9d0..b0f8807 100644
--- a/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
+++ b/packages/SystemUI/res/anim/recents_from_launcher_exit.xml
@@ -23,6 +23,6 @@
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true" android:fillAfter="true"
- android:interpolator="@android:interpolator/decelerate_cubic"
- android:duration="250"/>
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="100"/>
</set>
diff --git a/packages/SystemUI/res/anim/recents_from_unknown_enter.xml b/packages/SystemUI/res/anim/recents_from_unknown_enter.xml
new file mode 100644
index 0000000..f68a143
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_unknown_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="200"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_from_unknown_exit.xml b/packages/SystemUI/res/anim/recents_from_unknown_exit.xml
new file mode 100644
index 0000000..31cf26a
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_from_unknown_exit.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="normal">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="200"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_enter.xml b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
new file mode 100644
index 0000000..2857c04
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_to_launcher_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="normal">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="150"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recents_to_launcher_exit.xml b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
new file mode 100644
index 0000000..1139e72
--- /dev/null
+++ b/packages/SystemUI/res/anim/recents_to_launcher_exit.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@android:interpolator/fast_out_slow_in"
+ android:duration="150"/>
+</set>
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
deleted file mode 100644
index e6af81e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
index 873ca7e..fa5a0f0 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png
index 9d3cf53..d88397e 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
index 288d36a..59d4695 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
index 266d34d..bbebcb1 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
index 0298054..93dc268 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
index 91e2edf..0073872 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
index 9ed15a7..0073872 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
index 6f2915b..8a3d69c 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
index 3f3b692..0afd6fe 100644
--- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
new file mode 100644
index 0000000..d4fdbf3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png
deleted file mode 100644
index 7ed4c78..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
deleted file mode 100644
index 08c07b2..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
deleted file mode 100644
index 5ce8708..0000000
--- a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png
index 3f7c4b0..6ca3652 100644
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png
index 956bb7c..66f5c06 100644
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent.png
deleted file mode 100644
index 86e2947..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index d8ab8ea..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png
index a53aef1..e49a2af 100644
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png
index e1b2145..84879b9 100644
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent.png
deleted file mode 100644
index dffc059..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index c0209b0..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png
index 2d8f81d..d005f61 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png
index 61c6d11..dca30ab 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent.png
deleted file mode 100644
index a67ed6d..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index 6101333..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-hdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png
index 90eece6..6915a4a 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png
index 382cf23..309fd4e 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent.png
deleted file mode 100644
index ca1c8c4..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index cfef88d..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-mdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png
index a4b3b37..23b570e 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png
index ee5f623..1c5f73f 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent.png
deleted file mode 100644
index ca3c541..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index 78d4490..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xhdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png
index 3d21350..40cf808 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png
index 40fbaec..36dd2be 100644
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent.png
deleted file mode 100644
index 77bef31..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index d79f5b7..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-sw600dp-xxhdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png
index 61b4569..3b979cd 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png
index 1a0312b..e8d1dea 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent.png
deleted file mode 100644
index d04916e..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index d940d34..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png
index 8715a11..0ab0991 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png
index 487a0b1..4ced46b 100644
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent.png
deleted file mode 100644
index a7cff47..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent_land.png
deleted file mode 100644
index 0a60bb6..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_sysbar_recent_land.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
deleted file mode 100644
index e865f4c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
index 225d924..891397c5 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png
index 7779d57..5cf3855 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
index 37d17d2..edb5b1f 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
index 5e70a07..3212336 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
index 39a0c07..7aff620 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
index 5bbf3fe..9f66c14 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
index 798f62f..9f66c14 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
index f8e549a..972b1a8 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
index c7fda96..473b0cb 100644
--- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
new file mode 100644
index 0000000..9fc1a3b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png
deleted file mode 100644
index bd4e1ae..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
deleted file mode 100644
index e82c6e4..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
deleted file mode 100644
index bdf0f67..0000000
--- a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
index 3361e34..8ad60a5 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png
index ed52bc3..17cc722 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png
index b380327..6fa0d5f 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png
index 49df31b..70b3ef6 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png
index ac45bcd..be6a5ef 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png
index 9fb4266..b26600d 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png
index 459d011..b26600d 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png
index e1cddde..f2cb7be 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png
index e459f2c..170bdb6 100644
--- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
index 0ec4d23..b4bea6d 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png
index 4dedcbe..eb0eca1 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png
index e55f2bf..3d8d19b 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png
index a39c3e5..0f6fdad 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png
index 80e988f..cbe5f77 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png
index 4ef12b0d..80eb803 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png
index 8ef12a8..80eb803 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png
index c2977c0..a6d892f 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png
index 60ec10e..7183b81 100644
--- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
index 8aa6e3a..657d546 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png
index e272b62..df23594 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png
index bf68b22..ab27aec 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png
index 59ef663..31f98e1 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png
index cf3a5ff..104aed7 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png
index 2598954..18e113a 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png
index 66853db..18e113a 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png
index c46fd026..079e68d 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png
index 0e84d92..c38b43a 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png
index 7e96395e..c9507af 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png
index cb94580..2426776 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png
index 382ef39..48fb781 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png
index 8d7be53..c6fb9bf 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png
index 613fba0..528df6d 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png
index 0511ad1..055070a 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png
index 77fe9b2..055070a 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png
index ae78eb0..e17e8f4 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png
index 5b446b1..aafee03 100644
--- a/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-sw600dp-xxhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png
deleted file mode 100644
index 83693c1..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
index a2bb50a..e3cf7a6 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png
index 24897ce..7e034b8 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
index aaeeb1b..a4b3a684 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
index ddee461..321fadb 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
index 23a7997..6e3219f 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png
index 9f200c2..2b6021b 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
index 43e9bc2..2b6021b 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png
index 5c0ba82..023ca54 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
index b76a0ca..3c9dccb 100644
--- a/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
new file mode 100644
index 0000000..f38de93
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_data_bluetooth.png
deleted file mode 100644
index 757dbf3..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_data_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_data_bluetooth_connected.png
deleted file mode 100644
index d431dc2..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_data_bluetooth_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png
deleted file mode 100644
index 461535c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png
deleted file mode 100644
index cebbb15..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png
old mode 100644
new mode 100755
index 79cfcee..d8f1703
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png
old mode 100644
new mode 100755
index 7959f65..4e12cc9
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_ime.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
old mode 100644
new mode 100755
index c3bfcfb..37951a0
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_back_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png
old mode 100644
new mode 100755
index 64f6a22..7281482
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
old mode 100644
new mode 100755
index 8fd36bc..a13418c
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_home_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png
old mode 100644
new mode 100755
index 6f30e54..066c2d9
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png
old mode 100644
new mode 100755
index 024ef8f..a619a94
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_menu_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png
old mode 100644
new mode 100755
index 6e0b071..115109a
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
old mode 100644
new mode 100755
index 9a56987..3101cb0
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_sysbar_recent_land.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
new file mode 100644
index 0000000..8194605
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_data_bluetooth.png b/packages/SystemUI/res/drawable-xxhdpi/stat_sys_data_bluetooth.png
deleted file mode 100644
index 17ffdb9..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_data_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_data_bluetooth_connected.png b/packages/SystemUI/res/drawable-xxhdpi/stat_sys_data_bluetooth_connected.png
deleted file mode 100644
index 6ec234e..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_data_bluetooth_connected.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png
deleted file mode 100644
index 7b03a11..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_notify_zen.xml b/packages/SystemUI/res/drawable/ic_notify_zen.xml
new file mode 100644
index 0000000..c46455b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_notify_zen.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="24dp"
+ android:height="24dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_low.xml b/packages/SystemUI/res/drawable/ic_power_low.xml
new file mode 100644
index 0000000..5bb7aba
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_low.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="24.0dp"
+ android:height="24.0dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M30.0,6.0L30.0,2.0L18.0,2.0l0.0,4.0l-8.0,0.0l0.0,40.0l28.0,0.0L38.0,6.0L30.0,6.0zM26.0,37.0l-4.0,0.0l0.0,-4.0l4.0,0.0L26.0,37.0zM26.0,30.0l-4.0,0.0L22.0,15.0l4.0,0.0L26.0,30.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_saver.xml b/packages/SystemUI/res/drawable/ic_power_saver.xml
new file mode 100644
index 0000000..26e7375
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_saver.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="24.0dp"
+ android:height="24.0dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M30.0,6.0L30.0,2.0L18.0,2.0l0.0,4.0l-8.0,0.0l0.0,40.0l28.0,0.0L38.0,6.0L30.0,6.0zM32.0,28.0l-6.0,0.0l0.0,6.0l-4.0,0.0l0.0,-6.0l-6.0,0.0l0.0,-4.0l6.0,0.0l0.0,-6.0l4.0,0.0l0.0,6.0l6.0,0.0L32.0,28.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml b/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml
deleted file mode 100644
index dc30a53..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M18.939,7.244c-5.887,-5.885 -6.214,-6.214 -6.222,-6.222l-0.707,-0.737L5.088,7.207c-2.914,2.915 -3.74,6.629 -2.266,10.19c1.541,3.719 5.312,6.316 9.174,6.317l0.0,0.0c3.861,-0.001 7.636,-2.603 9.179,-6.328C22.646,13.834 21.832,10.138 18.939,7.244zM4.67,16.632c-1.149,-2.776 -0.481,-5.696 1.832,-8.011l5.494,-5.492c0.0,0.002 0.002,0.003 0.003,0.004l0.0,18.582c-0.001,0.0 -0.002,0.0 -0.003,0.0C8.922,21.714 5.91,19.624 4.67,16.632z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml
new file mode 100644
index 0000000..b6a5cad
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="64dp"
+ android:height="64dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#4DFFFFFF"
+ android:pathData="M41.3,41.7L36.6,37.0L24.0,24.5l-7.1,-7.1L14.0,14.5L8.5,9.0L6.0,11.5l5.6,5.6c-5.1,6.3 -4.7,15.5 1.1,21.4c3.1,3.1 7.2,4.7 11.3,4.7c3.6,0.0 7.1,-1.2 10.1,-3.6l5.4,5.4l2.5,-2.5L41.3,41.7zM24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5c0.0,-2.6 0.9,-5.1 2.4,-7.2l9.6,9.6L24.0,39.2zM24.0,10.2l0.0,9.2l14.5,14.5c2.7,-5.9 1.7,-13.1 -3.2,-18.0L24.0,4.5l0.0,0.0l0.0,0.0L16.6,12.0l2.8,2.8L24.0,10.2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
new file mode 100644
index 0000000..e8d59e0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="64dp"
+ android:height="64dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M35.3,15.9L24.0,4.5l0.0,0.0l0.0,0.0L12.7,15.9c-6.2,6.2 -6.2,16.4 0.0,22.6c3.1,3.1 7.2,4.7 11.3,4.7s8.2,-1.6 11.3,-4.7C41.6,32.2 41.6,22.1 35.3,15.9zM24.0,39.2L24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5s1.2,-6.2 3.5,-8.5l8.5,-8.5L24.0,39.2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_01.xml b/packages/SystemUI/res/drawable/ic_qs_location_01.xml
deleted file mode 100644
index ff37d9a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_01.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M12.0,2.0C8.13,2.0 5.0,5.13 5.0,9.0c0.0,5.25 7.0,13.0 7.0,13.0s7.0,-7.75 7.0,-13.0C19.0,5.13 15.87,2.0 12.0,2.0zM12.0,11.5c-1.38,0.0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5c1.38,0.0 2.5,1.12 2.5,2.5S13.38,11.5 12.0,11.5z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_02.xml b/packages/SystemUI/res/drawable/ic_qs_location_02.xml
deleted file mode 100644
index bb4465f..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_02.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M12.0,4.0c-3.48,0.0 -6.3,2.82 -6.3,6.3C5.7,15.02 12.0,22.0 12.0,22.0s6.3,-6.98 6.3,-11.7C18.3,6.82 15.48,4.0 12.0,4.0zM12.0,12.55c-1.24,0.0 -2.25,-1.01 -2.25,-2.25S10.76,8.05 12.0,8.05c1.24,0.0 2.25,1.01 2.25,2.25S13.24,12.55 12.0,12.55z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_03.xml b/packages/SystemUI/res/drawable/ic_qs_location_03.xml
deleted file mode 100644
index 956a8c3..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_03.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M12.0,7.0c-2.9,0.0 -5.25,2.35 -5.25,5.25C6.75,16.19 12.0,22.0 12.0,22.0s5.25,-5.81 5.25,-9.75C17.25,9.35 14.9,7.0 12.0,7.0zM12.0,14.12c-1.04,0.0 -1.88,-0.84 -1.88,-1.88s0.84,-1.88 1.88,-1.88c1.04,0.0 1.87,0.84 1.87,1.88S13.04,14.12 12.0,14.12z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_04.xml b/packages/SystemUI/res/drawable/ic_qs_location_04.xml
deleted file mode 100644
index 0c0fb3b..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_04.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M12.0,10.0c-2.32,0.0 -4.2,1.88 -4.2,4.2C7.8,17.35 12.0,22.0 12.0,22.0s4.2,-4.65 4.2,-7.8C16.2,11.88 14.32,10.0 12.0,10.0zM12.0,15.7c-0.83,0.0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5c0.83,0.0 1.5,0.67 1.5,1.5S12.83,15.7 12.0,15.7z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_05.xml b/packages/SystemUI/res/drawable/ic_qs_location_05.xml
deleted file mode 100644
index 1a21e2f..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_05.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M12.0,13.0c-1.74,0.0 -3.15,1.41 -3.15,3.15C8.85,18.51 12.0,22.0 12.0,22.0s3.15,-3.49 3.15,-5.85C15.15,14.41 13.74,13.0 12.0,13.0zM12.0,17.27c-0.62,0.0 -1.13,-0.5 -1.13,-1.12c0.0,-0.62 0.5,-1.12 1.13,-1.12c0.62,0.0 1.12,0.5 1.12,1.12C13.12,16.77 12.62,17.27 12.0,17.27z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_06.xml b/packages/SystemUI/res/drawable/ic_qs_location_06.xml
deleted file mode 100644
index 25c9ae5..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_06.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:fill="#FFFFFFFF"
- android:pathData="M12.0,16.0c-1.16,0.0 -2.1,0.94 -2.1,2.1C9.9,19.67 12.0,22.0 12.0,22.0s2.1,-2.33 2.1,-3.9C14.1,16.94 13.16,16.0 12.0,16.0zM12.0,18.85c-0.41,0.0 -0.75,-0.34 -0.75,-0.75s0.34,-0.75 0.75,-0.75c0.41,0.0 0.75,0.34 0.75,0.75S12.41,18.85 12.0,18.85z"/>
- <path
- android:pathData="M11.99,15c-1.35,0,-2.45,1.1,-2.45,2.45 c0,1.84,2.45,4.55,2.45,4.55s2.45,-2.71,2.45,-4.55C14.44,16.1,13.34,15,11.99,15z M11.99,18.33c-0.48,0,-0.88,-0.39,-0.88,-0.88 s0.39,-0.88,0.88,-0.88c0.48,0,0.87,0.39,0.87,0.88S12.47,18.33,11.99,18.33z"
- android:strokeWidth=".35"
- android:fill="#00000000"
- android:stroke="#CCCCCC"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_07.xml b/packages/SystemUI/res/drawable/ic_qs_location_07.xml
deleted file mode 100644
index a69c3a2..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_07.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:pathData="M12,9c-2.51,0,-4.55,2.04,-4.55,4.55 C7.45,16.96,12,22,12,22s4.55,-5.04,4.55,-8.45C16.55,11.04,14.51,9,12,9z M12,15.18c-0.9,0,-1.63,-0.73,-1.63,-1.62 s0.73,-1.62,1.63,-1.62c0.9,0,1.62,0.73,1.62,1.62S12.9,15.18,12,15.18z"
- android:strokeWidth="0.65"
- android:fill="#00000000"
- android:stroke="#CCCCCC"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_08.xml b/packages/SystemUI/res/drawable/ic_qs_location_08.xml
deleted file mode 100644
index c89c047..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_08.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:pathData="M12,6c-3.09,0,-5.6,2.51,-5.6,5.6 C6.4,15.8,12,22,12,22s5.6,-6.2,5.6,-10.4C17.6,8.51,15.09,6,12,6z M12,13.6c-1.1,0,-2,-0.9,-2,-2s0.9,-2,2,-2c1.1,0,2,0.9,2,2 S13.1,13.6,12,13.6z"
- android:strokeWidth="0.8"
- android:fill="#00000000"
- android:stroke="#CCCCCC"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_09.xml b/packages/SystemUI/res/drawable/ic_qs_location_09.xml
deleted file mode 100644
index 96bb6ce..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_09.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:pathData="M12,4c-3.48,0,-6.3,2.82,-6.3,6.3 C5.7,15.02,12,22,12,22s6.3,-6.98,6.3,-11.7C18.3,6.82,15.48,4,12,4z M12,12.55c-1.24,0,-2.25,-1.01,-2.25,-2.25S10.76,8.05,12,8.05 c1.24,0,2.25,1.01,2.25,2.25S13.24,12.55,12,12.55z"
- android:strokeWidth="0.9"
- android:fill="#00000000"
- android:stroke="#CCCCCC"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_10.xml b/packages/SystemUI/res/drawable/ic_qs_location_10.xml
deleted file mode 100644
index aced4bd..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_10.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:pathData="M12,3C8.33,3,5.35,5.98,5.35,9.65 C5.35,14.64,12,22,12,22s6.65,-7.36,6.65,-12.35C18.65,5.98,15.67,3,12,3z M12,12.02c-1.31,0,-2.38,-1.06,-2.38,-2.38 S10.69,7.28,12,7.28c1.31,0,2.37,1.06,2.37,2.37S13.31,12.02,12,12.02z"
- android:strokeWidth="0.95"
- android:fill="#00000000"
- android:stroke="#CCCCCC"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_11.xml b/packages/SystemUI/res/drawable/ic_qs_location_11.xml
deleted file mode 100644
index 578308e..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_location_11.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
- <size
- android:width="64dp"
- android:height="64dp"/>
-
- <viewport
- android:viewportWidth="24.0"
- android:viewportHeight="24.0"/>
-
- <path
- android:pathData="M12,2C8.13,2,5,5.13,5,9c0,5.25,7,13,7,13s7,-7.75,7,-13 C19,5.13,15.87,2,12,2z M12,11.5c-1.38,0,-2.5,-1.12,-2.5,-2.5s1.12,-2.5,2.5,-2.5c1.38,0,2.5,1.12,2.5,2.5S13.38,11.5,12,11.5z"
- android:fill="#00000000"
- android:stroke="#CCCCCC"
- android:strokeWidth="1.0"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_off.xml b/packages/SystemUI/res/drawable/ic_qs_location_off.xml
index d28d347..26ebfbf 100644
--- a/packages/SystemUI/res/drawable/ic_qs_location_off.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_location_off.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 The Android Open Source Project
@@ -14,18 +13,19 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<animation-list
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:oneshot="true">
- <item android:drawable="@drawable/ic_qs_location_01" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_02" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_03" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_04" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_05" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_06" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_07" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_08" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_09" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_10" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_11" android:duration="16" />
-</animation-list>
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="64dp"
+ android:height="64dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#4DFFFFFF"
+ android:pathData="M24.0,13.0c2.8,0.0 5.0,2.2 5.0,5.0c0.0,1.5 -0.7,2.8 -1.7,3.7l7.3,7.3c2.0,-3.7 3.4,-7.6 3.4,-11.0c0.0,-7.7 -6.3,-14.0 -14.0,-14.0c-4.0,0.0 -7.5,1.6 -10.1,4.3l6.4,6.4C21.2,13.6 22.5,13.0 24.0,13.0zM32.7,32.2l-9.3,-9.3l-0.2,-0.2L6.5,6.0L4.0,8.5l6.4,6.4c-0.2,1.0 -0.4,2.0 -0.4,3.1c0.0,10.5 14.0,26.0 14.0,26.0s3.3,-3.7 6.8,-8.7l6.7,6.7l2.5,-2.5L32.7,32.2z"/>
+ <path
+ android:pathData="M23.5,22.9l0.0,0.0 -0.20000076,-0.19999886z"
+ android:fill="#4DFFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_location_on.xml b/packages/SystemUI/res/drawable/ic_qs_location_on.xml
index 72512ac..bc73005 100644
--- a/packages/SystemUI/res/drawable/ic_qs_location_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_location_on.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2014 The Android Open Source Project
@@ -14,18 +13,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<animation-list
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:oneshot="true">
- <item android:drawable="@drawable/ic_qs_location_11" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_10" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_09" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_08" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_07" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_06" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_05" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_04" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_03" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_02" android:duration="16" />
- <item android:drawable="@drawable/ic_qs_location_01" android:duration="16" />
-</animation-list>
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="64dp"
+ android:height="64dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M24.0,4.0c-7.7,0.0 -14.0,6.3 -14.0,14.0c0.0,10.5 14.0,26.0 14.0,26.0s14.0,-15.5 14.0,-26.0C38.0,10.3 31.7,4.0 24.0,4.0zM24.0,23.0c-2.8,0.0 -5.0,-2.2 -5.0,-5.0s2.2,-5.0 5.0,-5.0c2.8,0.0 5.0,2.2 5.0,5.0S26.8,23.0 24.0,23.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/keyguard_overflow_number_background.xml b/packages/SystemUI/res/drawable/keyguard_overflow_number_background.xml
new file mode 100644
index 0000000..b812d43
--- /dev/null
+++ b/packages/SystemUI/res/drawable/keyguard_overflow_number_background.xml
@@ -0,0 +1,21 @@
+<?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
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="#1a000000" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/notification_row_legacy_bg.xml b/packages/SystemUI/res/drawable/notification_row_legacy_bg.xml
deleted file mode 100644
index ce3372e..0000000
--- a/packages/SystemUI/res/drawable/notification_row_legacy_bg.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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"
- android:exitFadeDuration="@android:integer/config_mediumAnimTime">
-
- <item android:state_pressed="true" android:drawable="@drawable/notification_item_background_color_pressed" />
- <item android:state_pressed="false" android:drawable="@drawable/notification_item_background_legacy_color" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/qs_panel_background.xml b/packages/SystemUI/res/drawable/qs_panel_background.xml
index a1a5362..74876f7 100644
--- a/packages/SystemUI/res/drawable/qs_panel_background.xml
+++ b/packages/SystemUI/res/drawable/qs_panel_background.xml
@@ -16,5 +16,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/system_primary_color" />
<corners
- android:radius="@*android:dimen/notification_quantum_rounded_rect_radius"/>
+ android:radius="@*android:dimen/notification_material_rounded_rect_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/ripple_drawable.xml b/packages/SystemUI/res/drawable/ripple_drawable.xml
index d2bff42..af7941f 100644
--- a/packages/SystemUI/res/drawable/ripple_drawable.xml
+++ b/packages/SystemUI/res/drawable/ripple_drawable.xml
@@ -16,6 +16,4 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:tint="?android:attr/colorControlHighlight"
- android:tintMode="src_over"
- android:pinned="true" />
+ android:color="?android:attr/colorControlHighlight" />
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth.xml
new file mode 100644
index 0000000..e28490b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="18dp"
+ android:height="18dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected.xml b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected.xml
new file mode 100644
index 0000000..c012d14
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_data_bluetooth_connected.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="18dp"
+ android:height="18dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M14.0,24.0l-4.0,-4.0l-4.0,4.0l4.0,4.0L14.0,24.0zM35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6zM38.0,20.0l-4.0,4.0l4.0,4.0l4.0,-4.0L38.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_no_sim.xml b/packages/SystemUI/res/drawable/stat_sys_no_sim.xml
new file mode 100644
index 0000000..70948b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_no_sim.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="18dp"
+ android:height="18dp"/>
+
+ <viewport
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"/>
+
+ <path
+ android:fill="#4DFFFFFF"
+ android:pathData="M19.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-7.0,0.0L7.7,5.3L19.0,16.7L19.0,5.0zM3.7,3.9L2.4,5.2L5.0,7.8L5.0,19.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c0.4,0.0 0.7,-0.1 1.0,-0.3l1.9,1.9l1.3,-1.3L3.7,3.9z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/status_bar_item_background.xml b/packages/SystemUI/res/drawable/status_bar_item_background.xml
deleted file mode 100644
index 3a50aa9..0000000
--- a/packages/SystemUI/res/drawable/status_bar_item_background.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 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.
--->
-
-<layer-list
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:opacity="translucent"
- >
- <item
- android:drawable="@drawable/notification_item_background_color"
- />
-</layer-list>
-
diff --git a/packages/SystemUI/res/drawable/ticker_background.xml b/packages/SystemUI/res/drawable/ticker_background.xml
deleted file mode 100644
index 7320fa0..0000000
--- a/packages/SystemUI/res/drawable/ticker_background.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2006 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.
--->
-
-<layer-list
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:opacity="translucent"
- >
- <!-- the large icon extends 12dp beyond the edge of the status bar -->
- <item
- android:drawable="@drawable/notification_item_background_color"
- android:top="12dp"
- />
-</layer-list>
-
diff --git a/packages/SystemUI/res/layout/battery_low.xml b/packages/SystemUI/res/layout/battery_low.xml
deleted file mode 100644
index 2373355..0000000
--- a/packages/SystemUI/res/layout/battery_low.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/layout/keyguard.xml
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/padding"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="16dp"
- >
-
- <TextView android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:textColor="#ffffffff"
- android:gravity="start"
- android:text="@string/battery_low_subtitle"
- />
-
- <TextView android:id="@+id/level_percent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:textColor="#ffffffff"
- android:gravity="start"
- android:paddingBottom="16dp"
- />
-
- <ImageView android:id="@+id/image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/battery_low_battery"
- />
-
-</LinearLayout>
-
-
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/res/layout/recents_empty.xml
index ac6450b..c0ddde1 100644
--- a/packages/SystemUI/res/layout/recents_empty.xml
+++ b/packages/SystemUI/res/layout/recents_empty.xml
@@ -21,7 +21,6 @@
android:gravity="center"
android:textSize="20sp"
android:textColor="#ffffffff"
- android:textStyle="italic"
android:text="@string/recents_empty_message"
android:fontFamily="sans-serif-light"
android:visibility="gone" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_nav_bar_scrim.xml b/packages/SystemUI/res/layout/recents_nav_bar_scrim.xml
new file mode 100644
index 0000000..4245d49
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_nav_bar_scrim.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal|bottom"
+ android:scaleType="fitXY"
+ android:src="@drawable/recents_lower_gradient" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 85d2f16..23f2796 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -56,6 +56,7 @@
android:layout_marginEnd="4dp"
android:layout_gravity="center_vertical|end"
android:padding="18dp"
+ android:visibility="invisible"
android:src="@drawable/recents_dismiss_light" />
</com.android.systemui.recents.views.TaskBarView>
</com.android.systemui.recents.views.TaskView>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index eaa2558..aa62daa 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -111,47 +111,13 @@
/>
</LinearLayout>
</LinearLayout>
-
- <LinearLayout android:id="@+id/ticker"
+
+ <ViewStub
+ android:id="@+id/ticker_stub"
+ android:inflatedId="@+id/ticker"
+ android:layout="@layout/status_bar_ticker"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingStart="6dip"
- android:animationCache="false"
- android:orientation="horizontal" >
- <ImageSwitcher android:id="@+id/tickerIcon"
- android:layout_width="@dimen/status_bar_icon_size"
- android:layout_height="@dimen/status_bar_icon_size"
- android:layout_marginEnd="4dip"
- >
- <com.android.systemui.statusbar.AnimatedImageView
- android:layout_width="@dimen/status_bar_icon_size"
- android:layout_height="@dimen/status_bar_icon_size"
- android:scaleType="center"
- />
- <com.android.systemui.statusbar.AnimatedImageView
- android:layout_width="@dimen/status_bar_icon_size"
- android:layout_height="@dimen/status_bar_icon_size"
- android:scaleType="center"
- />
- </ImageSwitcher>
- <com.android.systemui.statusbar.phone.TickerView android:id="@+id/tickerText"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:paddingTop="2dip"
- android:paddingEnd="10dip">
- <TextView
- android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- />
- <TextView
- android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- />
- </com.android.systemui.statusbar.phone.TickerView>
- </LinearLayout>
+ />
+
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 7f34041..d24d21e 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -35,12 +35,65 @@
android:background="@drawable/notification_header_bg"
android:clickable="true"
/>
+
+ <View android:id="@+id/header_spacer"
+ android:layout_height="8dp"
+ android:layout_width="0dp" />
+
+ <TextView
+ android:id="@+id/header_emergency_calls_only"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_below="@id/header_spacer"
+ android:paddingTop="12dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
+ android:visibility="gone"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
+ android:text="@*android:string/emergency_calls_only" />
+
+ <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
+ android:layout_width="40dp"
+ android:layout_height="@dimen/status_bar_header_height"
+ android:layout_alignParentEnd="true"
+ android:background="@null"
+ android:scaleType="centerInside"
+ android:padding="8dp" />
+
+ <ImageButton android:id="@+id/settings_button"
+ style="@android:style/Widget.Material.Button.Borderless"
+ android:layout_toStartOf="@id/multi_user_switch"
+ android:layout_width="56dp"
+ android:layout_height="@dimen/status_bar_header_height"
+ android:src="@drawable/ic_settings_24dp"
+ android:contentDescription="@string/accessibility_desc_quick_settings"/>
+
+ <FrameLayout android:id="@+id/system_icons_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/status_bar_header_height"
+ android:layout_toStartOf="@id/multi_user_switch"
+ android:layout_marginEnd="4dp"
+ android:layout_marginStart="16dp"
+ />
+
+ <TextView
+ android:id="@+id/header_charging_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@id/system_icons_container"
+ android:layout_below="@id/header_spacer"
+ android:paddingTop="12dp"
+ android:paddingEnd="16dp"
+ android:paddingStart="4dp"
+ style="@style/TextAppearance.StatusBar.Expanded.ChargingInfo"/>
+
<RelativeLayout
android:id="@+id/datetime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
- android:paddingTop="16dp"
+ android:layout_below="@id/header_emergency_calls_only"
+ android:paddingTop="8dp"
android:paddingBottom="16dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
@@ -65,30 +118,6 @@
/>
</RelativeLayout>
- <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
- android:layout_width="40dp"
- android:layout_height="@dimen/status_bar_header_height"
- android:layout_alignParentEnd="true"
- android:background="@null"
- android:scaleType="centerInside"
- android:padding="8dp"
- />
-
- <ImageButton android:id="@+id/settings_button"
- style="@android:style/Widget.Quantum.Button.Borderless"
- android:layout_toStartOf="@id/multi_user_switch"
- android:layout_width="56dp"
- android:layout_height="@dimen/status_bar_header_height"
- android:src="@drawable/ic_settings_24dp"
- android:contentDescription="@string/accessibility_desc_quick_settings"/>
-
- <FrameLayout android:id="@+id/system_icons_container"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/status_bar_header_height"
- android:layout_toStartOf="@id/multi_user_switch"
- android:layout_marginEnd="4dp"
- />
-
<com.android.keyguard.CarrierText
android:id="@+id/keyguard_carrier_text"
android:layout_width="match_parent"
@@ -105,6 +134,8 @@
layout="@layout/quick_settings_brightness_dialog"
android:id="@+id/brightness_container"
android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
/>
<TextView
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 f867068..c442f79 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -32,20 +32,20 @@
/>
<TextView
android:id="@+id/more_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_marginStart="20dp"
android:layout_gravity="center_vertical"
- android:gravity="center_horizontal"
- android:textColor="@color/keyguard_overflow_content_color"
- android:textAllCaps="true"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:background="@drawable/keyguard_overflow_number_background"
+ android:gravity="center"
+ android:textColor="#ff686868"
+ android:textStyle="bold"
+ android:textSize="14dp"
/>
<com.android.systemui.statusbar.NotificationOverflowIconsView
android:id="@+id/overflow_icons_view"
- android:layout_gravity="end|center_vertical"
- android:gravity="end"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="68dp"
android:layout_width="120dp"
android:layout_height="wrap_content"
/>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml
index ff8800c..e220a16 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml
@@ -18,48 +18,13 @@
<com.android.systemui.statusbar.SpeedBumpView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:focusable="true"
- android:clickable="true"
+ android:layout_height="@dimen/speed_bump_height"
android:visibility="gone"
>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/speed_bump_height_collapsed"
- android:layout_gravity="top"
- android:orientation="horizontal">
- <View
- android:id="@+id/speedbump_line_left"
- android:layout_width="0dp"
- android:layout_height="1dp"
- android:layout_weight="1"
- android:background="#6fdddddd"
- android:layout_gravity="center_vertical"/>
- <com.android.systemui.statusbar.SpeedBumpDotsLayout
- android:id="@+id/speed_bump_dots_layout"
- android:layout_width="34dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp"
- android:layout_height="match_parent"
- android:layout_weight="0"/>
- <View
- android:id="@+id/speedbump_line_right"
- android:layout_width="0dp"
- android:layout_height="1dp"
- android:layout_weight="1"
- android:background="#6fdddddd"
- android:layout_gravity="center_vertical"/>
- </LinearLayout>
- <TextView
- android:id="@+id/speed_bump_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|center_horizontal"
- android:fontFamily="sans-serif-condensed"
- android:textSize="15sp"
- android:singleLine="true"
- android:textColor="#eeeeee"
- android:visibility="invisible"
- android:text="@string/speed_bump_explanation"
- android:paddingTop="4dp" />
+ <com.android.systemui.statusbar.AlphaOptimizedView
+ android:id="@+id/speedbump_line"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="#6fdddddd"
+ android:layout_gravity="center_vertical"/>
</com.android.systemui.statusbar.SpeedBumpView>
diff --git a/packages/SystemUI/res/layout/status_bar_ticker.xml b/packages/SystemUI/res/layout/status_bar_ticker.xml
new file mode 100644
index 0000000..dd9b3ef
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_ticker.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ - Copyright 2014, The Android Open Source Project
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -->
+<LinearLayout android:id="@+id/ticker"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingStart="6dip"
+ android:animationCache="false"
+ android:orientation="horizontal">
+
+ <ImageSwitcher android:id="@+id/tickerIcon"
+ android:layout_width="@dimen/status_bar_icon_size"
+ android:layout_height="@dimen/status_bar_icon_size"
+ android:layout_marginEnd="4dip"
+ >
+ <com.android.systemui.statusbar.AnimatedImageView
+ android:layout_width="@dimen/status_bar_icon_size"
+ android:layout_height="@dimen/status_bar_icon_size"
+ android:scaleType="center"
+ />
+ <com.android.systemui.statusbar.AnimatedImageView
+ android:layout_width="@dimen/status_bar_icon_size"
+ android:layout_height="@dimen/status_bar_icon_size"
+ android:scaleType="center"
+ />
+ </ImageSwitcher>
+ <com.android.systemui.statusbar.phone.TickerView android:id="@+id/tickerText"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:paddingTop="2dip"
+ android:paddingEnd="10dip">
+ <TextView
+ android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ <TextView
+ android:textAppearance="@style/TextAppearance.StatusBar.PhoneTicker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ />
+ </com.android.systemui.statusbar.phone.TickerView>
+</LinearLayout>
+
diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
index 7671c354a..1928506 100644
--- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
+++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
@@ -28,7 +28,7 @@
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:button="@null"
- android:background="@*android:drawable/switch_track_quantum"
+ android:background="@*android:drawable/switch_track_material"
android:visibility="gone"
/>
<com.android.systemui.settings.ToggleSeekBar
diff --git a/packages/SystemUI/res/layout/user_switcher_item.xml b/packages/SystemUI/res/layout/user_switcher_item.xml
index 43a85e7..8df2f5a 100644
--- a/packages/SystemUI/res/layout/user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/user_switcher_item.xml
@@ -21,10 +21,12 @@
android:layout_width="match_parent"
android:layout_height="64dp"
android:orientation="horizontal"
+ android:gravity="center_vertical"
tools:context=".settings.UserSwitcherDialog">
<ImageView
- android:layout_width="64dp"
- android:layout_height="match_parent"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginStart="4dp"
android:id="@+id/user_picture"
tools:src="@drawable/dessert_zombiegingerbread"/>
<TextView
@@ -37,4 +39,11 @@
android:gravity="center_vertical"
tools:text="Hiroshi Lockheimer"
/>
+ <ImageView
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginEnd="4dp"
+ android:src="@*android:drawable/ic_menu_delete"
+ android:id="@+id/user_delete"
+ android:background="?android:attr/selectableItemBackground"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/volume_panel_item.xml b/packages/SystemUI/res/layout/volume_panel_item.xml
index 4a2a0c0..6e5ab47 100644
--- a/packages/SystemUI/res/layout/volume_panel_item.xml
+++ b/packages/SystemUI/res/layout/volume_panel_item.xml
@@ -35,7 +35,6 @@
android:layout_height="wrap_content"
android:layout_weight="1">
<SeekBar
- style="?android:attr/seekBarStyle"
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index c45254e..a2ef9e6 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Aan."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Af"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Gekoppel."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Koppel tans."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> toestelle)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth af"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Helderheid"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Outoroteer"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotasie gesluit"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Gesluit op portret"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Gesluit op landskap"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Outo-draai"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotasie is gesluit"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portret"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Landskap"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Invoermetode"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Ligging"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Ligging af"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nie gekoppel nie"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Geen netwerk nie"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi af"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Saai na skerm uit"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Uitsaaiskerm"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helderheid"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Keer kleure om"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"USB-verbinding"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Warmkol"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Kennisgewings"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"ONLANGS"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Geen onlangse programme nie"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Programinligting"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"soek"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk word\ndalk gemonitor"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Raak om te wys"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Moenie steur nie"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d meer"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Minder dringende kennisgewings hieronder"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tik weer om oop te maak"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Sleep op om te ontsluit"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Sleep regs vir foon"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Sleep links vir kamera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Totdat jy dit afskakel"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laai tans (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tot vol)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Een minuut lank"</item>
<item quantity="other" msgid="6924190729213550991">"%d minute lank"</item>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 5c4d98b..6aeb6d6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"በርቷል።"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"ጠፍቷል።"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"ተገናኝቷል።"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"በማገናኘት ላይ።"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"ብሉቱዝ <xliff:g id="STATE">%s</xliff:g>።"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"አካባቢ <xliff:g id="STATE">%s</xliff:g>።"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ማንቂያ ለ<xliff:g id="TIME">%s</xliff:g> ተዋቅሯል።"</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"ፓነል ዝጋ"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"ተጨማሪ ጊዜ"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ያነሰ ጊዜ"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G ውሂብ ቦዝኗል"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G ውሂብ ቦዝኗል"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"የተንቀሳቃሽ ውሂብ ቦዝኗል"</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"ብሉቱዝ (<xliff:g id="NUMBER">%d</xliff:g> መሣሪያዎች)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"ብሉቱዝ ጠፍቷል"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ብሩህነት"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"ራስ-አዙር"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"አዙሪት ተቆልፏል"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ወደ ቁመት አቀማመጥ ተቆልፏል"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ወደ መሬት ገጽታ ተቆልፏል"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"በራስ ሰር አሽከርክር"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"አዙሪት ተቆልፏል"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"ምስል ገላጭ"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"በወርድ"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"የግቤት ስልት"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"አካባቢ"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"አካባቢ ጠፍቷል"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"አልተገናኘም"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ምንም አውታረ መረብ የለም"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ጠፍቷል"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"ማያ ገጽ ውሰድ"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"ማያ ገጽ ውሰድ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ብሩህነት"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ራስ-ሰር"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"ቀለማትን ግልብጥ"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"ተጨማሪ ቅንብሮች"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"በማገናኘት ላይ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"መገናኛ ነጥብ"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"የቅርብ ጊዜዎች"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"ማሳወቂያዎች"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"ምንም የቅርብ ጊዜ መተግበሪያዎች የሉም"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"የመተግበሪያ መረጃ"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ፈልግ"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"አውታረ መረብ\nክትትል ሊደረግበት ይችላል"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"ለማሳየት ነካ ያድርጉ"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"አይረብሹ"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d ተጨማሪ"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"በጣም አስቸካይ ያልሆኑ ማሳወቂያዎች ከታች"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"ለመክፈት ዳግም መታ ያድርጉ"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"ለማስከፈት ወደ ላይ ያንሸራትቱ"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"ለስልክ ወደቀኝ ያንሸራትቱ"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"ለካሜራ ወደግራ ያንሸራትቱ"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"ይህን እስኪያጠፉት ድረስ"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ሃይል በመሙላት ላይ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> እስከሚሞላ ድረስ)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"ለአንድ ደቂቃ"</item>
+ <item quantity="other" msgid="6924190729213550991">"ለ%d ደቂቃዎች"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"ለአንድ ሰዓት"</item>
+ <item quantity="other" msgid="5408537517529822157">"ለ%d ሰዓቶች"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index d7c5651..1aec6fd 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"تم التشغيل."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"تم الإيقاف."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"متصل."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"جارٍ الاتصال."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"بلوتوث (<xliff:g id="NUMBER">%d</xliff:g> من الأجهزة)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"إيقاف البلوتوث"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"السطوع"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"تدوير تلقائي"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"تم قفل التدوير"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"تم القفل على الوضع العمودي"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"تم القفل على الوضع الأفقي"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"دوران تلقائي"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"تم قفل التدوير"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"عمودي"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"أفقي"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"أسلوب الإدخال"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"الموقع"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"الموقع قيد الإيقاف"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"ليست متصلة"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"لا تتوفر شبكة"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"إيقاف Wi-Fi"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"بث الشاشة"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"شاشة Cast"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"السطوع"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"تلقائي"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"قلب الألوان"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"النطاق"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"نقطة اتصال"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"الإشعارات"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"الأخيرة"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"ليست هناك تطبيقات حديثة"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"معلومات التطبيق"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"بحث"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"قد تكون الشبكة\nخاضعة للرقابة"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"المس للعرض"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"الرجاء عدم الإزعاج"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d أخرى"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"الإشعارات الأقل إلحاحًا أدناه"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"انقر مرة أخرى للفتح"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"مرر سريعًا لأعلى لإلغاء القفل"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"مرر سريعًا إلى اليسار لفتح الهاتف"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"مرر سريعًا إلى اليمين لفتح الكاميرا"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"لحين تعطيل هذا الإعداد"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"جارٍ الشحن (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> حتى الامتلاء)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"لمدة دقيقة واحدة"</item>
<item quantity="other" msgid="6924190729213550991">"لمدة %d من الدقائق"</item>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 2405804..e16cdd3 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Вкл."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Изкл."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Има връзка."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Установява се връзка."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> устройства)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth е изключен"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Яркост"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Автоматична ориентация"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Ориентацията е заключена"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Заключено във вертикален режим"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Заключено в хоризонтален режим"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматична ориентация"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Ориентацията е заключена"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Вертикален режим"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Хоризонтален режим"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Метод на въвеждане"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Местоположение"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Местоположението е изключено"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Няма връзка"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Няма мрежа"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi е изключен"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Екран за предаване"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Екран за предаване"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркост"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТ."</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Обръщане на цветовете"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Тетъринг"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка за достъп"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Известия"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"СКОРОШНИ"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Няма скорошни приложения"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Информация за приложението"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"търсене"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Мрежата може\nда се наблюдава"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Докоснете за показване"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Не ме безпокойте"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Още %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Ппоказване на по-малко спешните известия по-долу"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Докоснете отново, за да отворите"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Прекарайте пръст нагоре, за да отключите"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Прекарайте пръст надясно, за да използвате телефона"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Прекарайте пръст наляво, за да включите камерата"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Докато не изключите това"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарежда се (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до пълно зареждане)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"За една минута"</item>
<item quantity="other" msgid="6924190729213550991">"За %d минути"</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 982e0c6..ad08dab 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Activat."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Desactivat."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Connectat."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"S’està connectant."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> dispositius)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth desactivat"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brillantor"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotació automàtica"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotació bloquejada"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Bloquejat en mode vertical"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Bloquejat en mode horitzontal"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotació automàtica"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotació bloquejada"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertical"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horitzontal"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Mètode d\'entrada"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Ubicació"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Ubicació desactivada"</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Desconnectat"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No hi ha cap xarxa"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desconnectada"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Pantalla d\'emissió"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Pantalla d\'emissió"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillantor"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÀTICA"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverteix els colors"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Ancoratge a xarxa"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificacions"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"No hi ha aplicacions recents."</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informació de l\'aplicació"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"És possible que la xarxa\nestigui controlada"</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Toca per mostrar-ho."</string>
<string name="zen_mode_title" msgid="8793432092004749188">"No molesteu"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d més"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notificacions menys urgents a continuació"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Torna a tocar per obrir-la."</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Fes lliscar el dit cap amunt per desbloquejar el teclat."</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Fes lliscar el dit cap a la dreta per obrir el telèfon."</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Fes lliscar el dit cap a l\'esquerra per obrir la càmera."</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Fins que no ho desactivis"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregant (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> per completar la càrrega)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durant un minut"</item>
<item quantity="other" msgid="6924190729213550991">"Durant %d minuts"</item>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 570e1bb..5a08251 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Zapnuto."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Vypnuto."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Připojeno."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Připojování."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> zařízení)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Rozhraní Bluetooth je vypnuto"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Jas"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatické otáčení"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Otáčení je uzamčeno"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Uzamčen režim na výšku"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Uzamčen režim na šířku"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatické otáčení"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Otáčení je uzamčeno"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Na výšku"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Na šířku"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Metoda zadávání dat"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Poloha"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Poloha vypnuta"</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nepřipojeno"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Žádná síť"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi vypnuta"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Vzdálená obrazovka"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Odesílání obrazovky"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jas"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATICKY"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Převrátit barvy"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Sdílení datového připojení"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Oznámení"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"POSLEDNÍ"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Žádné nedávné aplikace"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informace o aplikaci"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Síť může být\nmonitorována"</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Oznámení zobrazíte kliknutím"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Nerušit"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Další: %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Méně urgentní oznámení níže"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Oznámení otevřete opětovným klepnutím"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Zařízení odemknete přejetím prstem nahoru"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Telefon otevřete přejetím prstem vpravo."</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Fotoaparát otevřete přejetím prstem vlevo."</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dokud tuto funkci nevypnete"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nabíjení (plně nabito za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Na jednu minutu"</item>
<item quantity="other" msgid="6924190729213550991">"Na %d min"</item>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 127192a..50c3dbb 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Til."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Fra."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Forbundet."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Opretter forbindelse..."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> enheder)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth slået fra"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Lysstyrke"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatisk rotation"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotation er låst"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Altid stående"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Altid liggende"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Roter automatisk"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotationen er låst"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Stående"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Liggende"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Inputmetode"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Placering"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Placering fra"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ikke forbundet"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Intet netværk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi slået fra"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast skærm"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Skærm til casting"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Byt om på farver"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Netdeling"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Underretninger"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"SENESTE"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Der er ingen seneste apps"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Oplysninger om applikationen"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"søg"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netværket kan\nvære overvåget"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Tryk for at vise"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Vil ikke forstyrres"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d mere"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre presserende underretninger nedenfor"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tryk igen for at åbne"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Stryg for at låse op"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Stryg til højre for at bruge telefonen"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Stryg til venstre for at åbne kameraet"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Indtil du slår denne indstilling fra"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Opladning (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"I ét minut"</item>
<item quantity="other" msgid="6924190729213550991">"I %d minutter"</item>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index c9c7b8e..27523d8 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"An"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Aus"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Verbunden"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Verbindung wird hergestellt."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Geräte)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth aus"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Helligkeit"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Autom. drehen"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Drehung gesperrt"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Auf Hochformat gesperrt"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Auf Querformat gesperrt"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatisch drehen"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Drehung gesperrt"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Hochformat"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Querformat"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Eingabemethode"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Standort"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Standort aus"</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nicht verbunden"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Kein Netz"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WLAN aus"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Bildschirm übertragen"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Bildschirmübertragung"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helligkeit"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Farben umkehren"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Benachrichtigungen"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"Letzte"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Keine neuen Apps"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"App-Info"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netzwerk wird\neventuell überwacht."</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Zum Ansehen tippen"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Nicht stören"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d mehr"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Weniger dringende Benachrichtigungen unten"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Erneut tippen, um Benachrichtigung zu öffnen"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Zum Entsperren nach oben wischen"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Zum Öffnen des Telefons nach rechts wischen"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Zum Öffnen der Kamera nach links wischen"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Bis zur Deaktivierung"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Wird aufgeladen (voll in <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Für eine Minute"</item>
<item quantity="other" msgid="6924190729213550991">"Für %d Minuten"</item>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 1d90dae..c0d586e 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Ενεργό."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Ανενεργό."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Έχει συνδεθεί."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Σύνδεση."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> συσκευές)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Απενεργοποιημένο Bluetooth"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Φωτεινότητα"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Αυτόματη περιστροφή"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Η περιστροφή είναι κλειδωμένη"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Κλειδωμένο στην κατακόρυφη προβολή"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Κλειδωμένο στην οριζόντια προβολή"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Αυτόματη περιστροφή"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Η περιστροφή είναι κλειδωμένη"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Κατακόρυφα"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Οριζόντια"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Μέθοδος εισαγωγής"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Τοποθεσία"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Τοποθεσία απενεργοποιημένη"</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Μη συνδεδεμένο"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Κανένα δίκτυο"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ανενεργό"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Μετάδοση οθόνης"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Μετάδοση οθόνης"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Φωτεινότητα"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ΑΥΤΟΜΑΤΗ"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Αντιστροφή χρωμάτων"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Πρόσδεση"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Σημείο πρόσβασης Wi-Fi"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ειδοποιήσεις"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"ΠΡΟΣΦΑΤΑ"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Καμία πρόσφατη εφαρμογή"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Πληροφορίες εφαρμογής"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Το δίκτυο μπορεί\nνα παρακολουθείται"</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Αγγίξτε για εμφάνιση"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Μην ενοχλείτε"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d ακόμη"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Λιγότερο επείγουσες ειδοποιήσεις παρακάτω"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Πατήστε ξανά για να ανοίξετε"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Σύρετε για να ξεκλειδώσετε"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Σύρετε προς τα δεξιά για το τηλέφωνο"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Σύρετε αριστερά για τη φωτογραφική μηχανή"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Μέχρι να το απενεργοποιήσετε"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Φόρτιση (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> για πλήρη φόρτιση)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Για ένα λεπτό"</item>
<item quantity="other" msgid="6924190729213550991">"Για %d λεπτά"</item>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index a9c6678..df8698c 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"On."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Off."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Connected."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Connecting."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Devices)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth Off"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Auto Rotate"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotation Locked"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Locked to Portrait"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Locked to Landscape"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Auto-rotate"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotation locked"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portrait"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Landscape"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Input Method"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Location"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Location Off"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Cast screen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invert colours"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"No recent apps"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Network may\nbe monitored"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Touch to show"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Do not disturb"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d more"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Swipe right for phone"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Swipe left for camera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"For one minute"</item>
<item quantity="other" msgid="6924190729213550991">"For %d minutes"</item>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index a9c6678..df8698c 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"On."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Off."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Connected."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Connecting."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Devices)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth Off"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Auto Rotate"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotation Locked"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Locked to Portrait"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Locked to Landscape"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Auto-rotate"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotation locked"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portrait"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Landscape"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Input Method"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Location"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Location Off"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Not Connected"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No Network"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Cast screen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invert colours"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTS"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"No recent apps"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Application Info"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"search"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Network may\nbe monitored"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Touch to show"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Do not disturb"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d more"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Swipe up to unlock"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Swipe right for phone"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Swipe left for camera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charging (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> until full)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"For one minute"</item>
<item quantity="other" msgid="6924190729213550991">"For %d minutes"</item>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 6ca4ebc..5ce867e 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Activado"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Desactivado"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Conectado"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Conectando"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> dispositivos)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth desactivado"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brillo"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Girar automáticamente"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotación bloqueada"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Bloqueada en vertical"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Bloqueada en horizontal"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotación automática"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotación bloqueada"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertical"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horizontal"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de introducción"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Ubicación"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Ubicación desactivada"</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Sin conexión"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sin red"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desactivada"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Pantalla de Cast"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Transmitir pantalla"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillo"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertir colores"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Anclaje a red"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECIENTES"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"No hay aplicaciones recientes."</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Es posible que la red\nesté supervisada."</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Toca para mostrar"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"No molestar"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d más"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notificaciones menos urgentes abajo"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Presionar de nuevo para abrir"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar el dedo hacia arriba para desbloquear"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Desliza hacia la derecha para abrir el teléfono."</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Desliza hacia la izquierda para acceder a la cámara."</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hasta que lo desactives"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (faltan <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> para completar)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index d0e9c0a..c25e5cd 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Sí"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"No"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Conectado"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Conectando."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> dispositivos)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth desactivado"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brillo"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Girar automáticamente"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotación bloqueada"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Bloqueado en vertical"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Bloqueado en horizontal"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Girar automáticamente"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotación bloqueada"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertical"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horizontal"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de entrada"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Ubicación"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Ubicación desactivada"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"No conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No hay red."</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desactivado"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Enviar contenido a pantalla"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Pantalla de Cast"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brillo"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertir colores"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Anclaje a red"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECIENTES"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"No hay aplicaciones recientes"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Información de la aplicación"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La red se\npuede supervisar"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Toca para mostrar"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"No molestar"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d más"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notificaciones menos urgente abajo"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Toca de nuevo para abrir"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Desliza el dedo hacia arriba para desbloquear"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Desliza el dedo hacia la izquierda para acceder al teléfono"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Desliza el dedo hacia la izquierda para acceder a la cámara"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hasta apagar el dispositivo"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Cargando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hasta completar)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durante un minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index c692b28..21cb2f1 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Sees."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Väljas."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Ühendatud."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Ühenduse loomine."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> seadet)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth on väljas"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Heledus"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automaatne pööramine"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Pööramine lukus"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Lukustatud vertikaalpaigutusse"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Lukustatud horisontaalpaigutusse"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automaatne pööramine"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Pööramine on lukustatud"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertikaalpaigutus"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horisontaalpaigutus"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Sisestusmeetod"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Asukoht"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Asukoht on väljas"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ühendus puudub"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Võrku pole"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WiFi-ühendus on väljas"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast-ekraan"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Ülekandeekraan"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Heledus"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAATNE"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Vaheta värve"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Jagamine"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Leviala"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Märguanded"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"HILJUTISED"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Hiljutisi rakendusi pole"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Rakenduste teave"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Võrku võidakse\njälgida"</string>
@@ -222,13 +223,14 @@
<!-- no translation found for zen_mode_notification_title:other (7388721375827338153) -->
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Puudutage kuvamiseks"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Mitte segada"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Veel %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Vähem kiireloomulised märguanded on allpool"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Avamiseks puudutage uuesti"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Lukustuse tühistamiseks pühkige üles"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Telefoni kasutamiseks pühkige paremale"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Kaamera kasutamiseks pühkige vasakule"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Kuni lülitate selle välja"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laadimine (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>, kuni seade on täis)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Üheks minutiks"</item>
<item quantity="other" msgid="6924190729213550991">"%d minutiks"</item>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index ee2c48d..a0d1819 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"روشن."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"خاموش."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"متصل."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"در حال مرتبط شدن."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"بلوتوث ( <xliff:g id="NUMBER">%d</xliff:g> دستگاه)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"بلوتوث خاموش"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"روشنایی"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"چرخش خودکار"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"چرخش قفل شد"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"قفل شده در حالت عمودی"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"قفل شده در حالت افقی"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"چرخش اتوماتیک"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"چرخش قفل شد"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"عمودی"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"افقی"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"روش ورودی"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"مکان"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"مکان خاموش"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"متصل نیست"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"شبکهای موجود نیست"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi خاموش است"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"فرستادن صفحه"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"فرستادن صفحهنمایش"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"روشنایی"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"خودکار"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"برگردان رنگها"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"اتصال به اینترنت با تلفن همراه"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"نقطه اتصال"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"اعلانها"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"موارد اخیر"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"هیچ برنامه جدیدی موجود نیست"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"اطلاعات برنامه"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"جستجو"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"ممکن است شبکه\nتحت نظارت باشد"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"برای نمایش لمس کنید"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"مزاحم نشوید"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d بیشتر"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"اعلانهای کمتر فوری در زیر"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"برای باز کردن دوباره ضربه بزنید"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"برای باز کردن قفل سریع به بالا بکشید"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"برای تلفن انگشت را تند به سمت چپ بکشید"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"برای دوربین انگشت را تند به سمت راست بکشید"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"تا وقتی آن را خاموش کنید"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"در حال شارژ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> تا شارژ کامل)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"برای یک دقیقه"</item>
<item quantity="other" msgid="6924190729213550991">"برای %d دقیقه"</item>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 44d35b3..0a0cdd9 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Käytössä."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Pois käytöstä."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Yhdistetty."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Yhdistetään."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> laitetta)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth pois käytöstä"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Kirkkaus"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automaattinen kääntö"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Kääntö lukittu"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Vaaka lukittu"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Pysty lukittu"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automaattinen kääntö"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Kääntö lukittu"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Pysty"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Vaaka"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Syöttötapa"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Sijainti"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Sijainti ei käytössä"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ei yhteyttä"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ei verkkoa"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi-yhteys pois käytöstä"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Lähetysnäyttö"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Lähetysnäyttö"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kirkkaus"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Käänteiset värit"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Jaettu yhteys"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ilmoitukset"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"VIIMEISIMMÄT"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Ei viimeaikaisia sovelluksia"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Sovellustiedot"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Verkkoa saatetaan\nvalvoa"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Näytä koskettamalla"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Älä häiritse"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d muuta"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Vähemmän kiireelliset ilmoitukset ovat alla"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Avaa napauttamalla uudelleen"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Avaa lukitus pyyhkäisemällä ylös"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Avaa puhelin pyyhkäisemällä oikealle"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Avaa kamera pyyhkäisemällä oikealle"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Kunnes poistat tämän käytöstä"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ladataan (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kunnes täynnä)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Minuutiksi"</item>
<item quantity="other" msgid="6924190729213550991">"%d minuutiksi"</item>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index d9790f6..c3b53b5 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Activé"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Désactivé"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Connecté"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Connexion."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1x"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"3G+"</string>
@@ -158,12 +159,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth : <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localisation <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme réglée sur <xliff:g id="TIME">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fermer le panneau"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Plus longtemps"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Moins longtemps"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Données 2G-3G désactivées"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Données 4G désactivées"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Données mobiles désactivées"</string>
@@ -190,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> appareils)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"BLUETOOTH DÉSACTIVÉ"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminosité"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotation automatique"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotation verrouillée"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Verrouillé en mode portrait"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Verrouillé en mode paysage"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotation automatique"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotation verrouillée"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portrait"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Paysage"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Mode de saisie"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Position"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localisation désactivée"</string>
@@ -207,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Non connecté"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Aucun réseau"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi désactivé"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Écran distant"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Écran de diffusion"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosité"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATIQUE"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverser les couleurs"</string>
@@ -215,9 +213,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Plus de paramètres"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Partage de connexion"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Point d\'accès sans fil"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"RÉCENTS"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Aucune application récente"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Détails de l\'application"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Le réseau peut\nêtre surveillé."</string>
@@ -230,16 +227,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Touchez pour afficher la notification"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Ne pas déranger"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d autres"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notifications moins urgentes affichées ci-dessous"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Touchez à nouveau pour ouvrir"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Glissez vers le haut pour déverrouiller"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Balayez l\'écran vers la droite pour accéder au téléphone"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Balayez l\'écran vers la gauche pour accéder à l\'appareil photo"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours... (chargée à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
+ <item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Pendant une heure"</item>
+ <item quantity="other" msgid="5408537517529822157">"Pendant %d heures"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index d95fe06..ab1f30b 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Activé"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Désactivé"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Connecté"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Connexion en cours…"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1x"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,12 +159,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth : <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localisation <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme réglée sur <xliff:g id="TIME">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fermer le panneau"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Plus longtemps"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Moins longtemps"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Données 2G-3G désactivées"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Données 4G désactivées"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Données mobiles désactivées"</string>
@@ -190,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> appareils)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth désactivé"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminosité"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotation auto"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotation bloquée"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Verrouillé en mode portrait"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Verrouillé en mode paysage"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotation automatique"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotation verrouillée"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portrait"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Paysage"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Mode de saisie"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Localisation"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localisation désactivée"</string>
@@ -207,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Non connecté"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Aucun réseau"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi désactivé"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Caster l\'écran"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Caster l\'écran"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosité"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATIQUE"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverser les couleurs"</string>
@@ -215,9 +213,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Plus de paramètres"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Partage de connexion"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Point d\'accès"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"RÉCENTS"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifications"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Aucune application récente"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informations sur l\'application"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Le réseau peut\nêtre surveillé."</string>
@@ -230,16 +227,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Appuyer pour afficher"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Ne pas déranger"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"+ %d autres"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notifications moins urgentes ci-dessous"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Appuyer à nouveau pour ouvrir"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Faire glisser pour déverrouiller"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Balayer l\'écran vers la droite pour accéder au téléphone"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Balayer l\'écran vers la gauche pour accéder à l\'appareil photo"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Pendant une minute"</item>
+ <item quantity="other" msgid="6924190729213550991">"Pendant %d minutes"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Pendant une heure"</item>
+ <item quantity="other" msgid="5408537517529822157">"Pendant %d heures"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 60d7de1..8b1eb55b 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"चालू."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"बंद."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"कनेक्ट है."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"कनेक्ट हो रहा है."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> उपकरण)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth बंद"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"स्क्रीन की रोशनी"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"स्वत: रोटेट"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"रोटेशन लॉक किया गया"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"पोर्ट्रेट पर लॉक किया गया"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"भू-दृश्य पर लॉक किया गया"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"स्वत: घुमाएं"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"घुमाना लॉक किया गया"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"पोर्ट्रेट"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"लैंडस्केप"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"इनपुट विधि"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"स्थान"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"स्थान बंद"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"कनेक्ट नहीं है"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"कोई नेटवर्क नहीं"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi बंद"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"स्क्रीन कास्ट करें"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"स्क्रीन कास्ट करें"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"स्क्रीन की रोशनी"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"स्वत:"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"रंग उलटें"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"टेदरिंग"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"हॉटस्पॉट"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"सूचनाएं"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"हाल ही का"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"कोई हाल ही का ऐप्स नहीं"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"एप्लिकेशन जानकारी"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"खोज"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"नेटवर्क को\nमॉनीटर किया जा सकता है"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"दिखाने के लिए स्पर्श करें"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"परेशान न करें"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d और"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"कम अत्यावश्यक सूचनाएं नीचे दी गई हैं"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"खोलने के लिए पुन: टैप करें"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"अनलॉक करने के लिए ऊपर स्वाइप करें"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"फ़ोन के लिए दाएं स्वाइप करें"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"कैमरे के लिए बाएं स्वाइप करें"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"जब तक आप इसे बंद नहीं कर देते"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"चार्ज हो रहा है (पूर्ण होने में <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> शेष)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"एक मिनट के लिए"</item>
<item quantity="other" msgid="6924190729213550991">"%d मिनट के लिए"</item>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 09dde42..953e077 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Uključeno."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Isključeno."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Povezano."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Povezivanje."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (broj uređaja: <xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth isključen"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Svjetlina"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatska rotacija"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotacija zaključana"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Blokirano u portretnom prikazu"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Blokirano u pejzažnom prikazu"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatsko usmjerenje"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Usmjerenje je zaključano"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Okomito"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Vodoravno"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Način unosa"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Lokacija"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Lokacija je isključena"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nije povezano"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nema mreže"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi isključen"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Emitiranje zaslona"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Emitirani zaslon"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Svjetlina"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATSKI"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Preokreni boje"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Dijeljenje veze"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Žarišna točka"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavijesti"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"NEDAVNO"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Nema nedavnih aplikacija"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacije o aplikaciji"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Mreža se\nmožda prati"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Dodirnite za prikaz"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Ne ometaj"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Još %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Manje hitne obavijesti pri dnu"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Dodirnite opet za otvaranje"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Prijeđite prstom prema gore za otključavanje"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Prijeđite prstom udesno za telefon"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Prijeđite prstom ulijevo za fotoaparat"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dok ne isključite"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Punjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napunjenosti)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Jednu minutu"</item>
<item quantity="other" msgid="6924190729213550991">"%d min"</item>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index dc85416..ad2bc5c 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Bekapcsolva."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Kikapcsolva."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Csatlakoztatva."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Csatlakozás."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> eszköz)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth kikapcsolva"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Fényerő"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatikus forgatás"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Forgatás zárolva"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Álló nézet zárolva"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Fekvő nézet zárolva"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatikus elforgatás"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Elforgatás zárolva"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Álló"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Fekvő"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Beviteli módszer"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Tartózkodási hely"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Hely kikapcsolva"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nincs kapcsolat"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nincs hálózat"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi kikapcsolva"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Képernyő tartalmának átküldése"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Képernyőtartalom átküldése"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Fényerő"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"automatikus"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Színek invertálása"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Megosztás"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Értesítések"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"LEGUTÓBBIAK"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Nincsenek nemrég használt alkalmazások"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Az alkalmazás adatai"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"keresés"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Lehet, hogy a\nhálózat felügyelt"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"A megtekintéshez érintse meg"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Ne zavarjanak"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d további"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"A kevésbé sürgős értesítések lentebb vannak"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Koppintson rá ismét a megnyitáshoz"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Húzza felfelé az ujját a feloldáshoz"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"A telefon eléréséhez csúsztassa ujját jobbra"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"A fényképezőgép eléréséhez csúsztassa ujját balra"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Amíg ki nem kapcsolja ezt"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Töltés (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> a teljes töltöttségig)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Egy percen át"</item>
<item quantity="other" msgid="6924190729213550991">"%d percen át"</item>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index b11a004..e15730e 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Միացված է:"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Անջատված է:"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Միացված է:"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Միանում է:"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth-ը <xliff:g id="STATE">%s</xliff:g> է:"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Տեղադրությունը՝ <xliff:g id="STATE">%s</xliff:g>:"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Զարթուցիչը դրված է <xliff:g id="TIME">%s</xliff:g>-ին:"</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Փակել վահանակը"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Ավելացնել ժամանակը"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Քչացնել ժամանակը"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G տվյալները անջատված են"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G տվյալները անջատված են"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Շարժական տվյալները անջատված են"</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> սարք)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth-ն անջատված է"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Պայծառություն"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Ինքնապտտում"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Պտտումը կողպված է"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Միայն ուղղաձիգ"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Միայն հորիզոնական"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Ինքնապտտում"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Պտտումը կողպված է"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Դիմանկար"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Լանդշաֆտ"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Մուտքագրման եղանակը"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Տեղադրություն"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Անջատել տեղադրությունը"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Միացված չէ"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ցանց չկա"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi-ը անջատված է"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Հեռակա էկրան"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Հեռակա էկրան"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Պայծառություն"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"Ինքնաշխատ"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Շրջել գույները"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Հավելյալ կարգավորումներ"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Միացում"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Թեժ կետ"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"ՎԵՐՋԻՆՆԵՐԸ"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Ծանուցումներ"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Նոր հավելվածներ չկան"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Հավելվածի մասին"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Ցանցը կարող է\nվերահսկվել"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Հպեք՝ ցուցադրելու համար"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Չխանգարել"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Եվս %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Պակաս հրատապ ծանուցումները ստորև"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Կրկին հպեք՝ բացելու համար"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Սահեցրեք վերև` ապակողպելու համար"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Հեռախոսի համար սահեցրեք աջ"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Խցիկի համար սահեցրեք ձախ"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Քանի դեռ չեք անջատել"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> մինչև լրիվ լիցքավորումը)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Մեկ րոպե"</item>
+ <item quantity="other" msgid="6924190729213550991">"%d րոպե"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Մեկ ժամ"</item>
+ <item quantity="other" msgid="5408537517529822157">"%d ժամ"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index f62209f..08da900 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Aktif."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Nonaktif."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Tersambung."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Menyambung."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Perangkat)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth Mati"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Kecerahan"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotasi Otomatis"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotasi Dikunci"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Terkunci ke Potret"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Terkunci ke Lanskap"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotasi otomatis"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotasi terkunci"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Potret"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Lanskap"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Metode Masukan"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Lokasi"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Lokasi Mati"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Tidak Tersambung"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tidak Ada Jaringan"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Mati"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Layar Transmisi"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Layar transmisi"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATIS"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inversi warna"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Menambatkan"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pemberitahuan"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"TERBARU"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Tidak ada aplikasi terkini"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Info Aplikasi"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Jaringan bisa\ndiawasi"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Sentuh untuk menampilkan"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Jangan ganggu"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d lainnya"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Pemberitahuan kurang darurat di bawah"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ketuk lagi untuk membuka"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Gesek ke atas untuk membuka kunci"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Gesek ke kanan untuk menelepon"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Gesek ke kiri untuk kamera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Hingga Anda menonaktifkan ini"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengisi daya (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hingga penuh)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Selama satu menit"</item>
<item quantity="other" msgid="6924190729213550991">"Selama %d menit"</item>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 6cf13be..16ebe4c 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"ON"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"OFF"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Connesso."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Connessione in corso."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> dispositivi)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth spento"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminosità"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotazione autom."</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotazione bloccata"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Bloccato in verticale"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Bloccato in orizzontale"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotazione automatica"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotazione bloccata"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Verticale"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Orizzontale"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Metodo di immissione"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Geolocalizz."</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Geolocalizz. non attiva"</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Non connesso"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nessuna rete"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi disattivato"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Trasmetti schermo"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Trasmetti schermo"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminosità"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverti colori"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notifiche"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"MESSAGGI RECENTI"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Nessuna app recente"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informazioni sull\'applicazione"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La rete potrebbe\nessere monitorata"</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Tocca per visualizzare"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Non disturbare"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Altre %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notifiche meno urgenti in basso"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tocca ancora per aprire"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Scorri verso l\'alto per sbloccare"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Scorri verso destra per accedere al telefono"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Scorri verso sinistra per accedere alla fotocamera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Fino alla disattivazione"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"In carica (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> al termine)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Per un minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Per %d minuti"</item>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 4f07006..49d7b40 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"פועל."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"כבוי."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"מחובר."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"מתחבר."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> מכשירים)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth מופסק"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"בהירות"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"סיבוב אוטומטי"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"סיבוב נעול"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"נעול במצב הצגה לאורך"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"נעול במצב הצגה לרוחב"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"סיבוב אוטומטי"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"סיבוב נעול"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"לאורך"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"לרוחב"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"שיטת קלט"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"מיקום"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"מיקום כבוי"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"לא מחובר"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"אין רשת"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi כבוי"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"העבר מסך"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"העבר מסך"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"בהירות"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"אוטומטי"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"הפוך צבעים"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"שיתוף אינטרנט בין ניידים"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"נקודה לשיתוף אינטרנט"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"הודעות"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"אחרונים"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"אין אפליקציות אחרונות"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"מידע על האפליקציה"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"חפש"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"ייתכן שהרשת\nמנוטרת"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"גע כדי להציג"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"נא לא להפריע"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"עוד %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"הודעות בדחיפות נמוכה יותר בהמשך"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"הקש שוב כדי לפתוח"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"החלק מעלה כדי לבטל את הנעילה"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"החלק ימינה להפעלת הטלפון"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"החלק שמאלה להפעלת המצלמה"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"עד שתכבה"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"טוען (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> עד לסיום)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"למשך דקה אחת"</item>
<item quantity="other" msgid="6924190729213550991">"למשך %d דקות"</item>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 84bbf8b..4a2a3c1 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"ON"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"OFF"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"接続済みです。"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"接続しています。"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,12 +159,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"現在地: <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"アラームは<xliff:g id="TIME">%s</xliff:g>に設定されています。"</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"パネルを閉じる"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"長くする"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"短くする"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G~3Gデータが無効になりました"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4Gデータが無効になりました"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"モバイルデータが無効になりました"</string>
@@ -190,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth(端末数<xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth OFF"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"画面の明るさ"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"自動回転"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"画面の向きをロック"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"縦向きにロック済み"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"横向きにロック済み"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自動回転"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"画面の向きをロック"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"縦向き"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"横向き"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"入力方法"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"現在地"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"現在地OFF"</string>
@@ -207,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"接続されていません"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ネットワークなし"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi OFF"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"画面のキャスト"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"画面のキャスト"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"画面の明るさ"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"色を反転"</string>
@@ -215,9 +213,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"詳細設定"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"テザリング"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"アクセスポイント"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"最近"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"最近使ったアプリはありません"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"アプリ情報"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"検索"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"ネットワークが監視される\n場合があります"</string>
@@ -230,16 +227,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"表示するにはタップします"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"通知を非表示"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"他%d件"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"緊急度の低い通知を下に表示"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"開くにはもう一度タップしてください"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"ロック解除するには上にスワイプしてください"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"右にスワイプして電話を表示"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"左にスワイプしてカメラを表示"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"ユーザーがOFFにするまで"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中(フルになるまで<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"1分"</item>
+ <item quantity="other" msgid="6924190729213550991">"%d分"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"1時間"</item>
+ <item quantity="other" msgid="5408537517529822157">"%d時間"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index c31ea24..079fc7f 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"ჩართული"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"გამორთულია."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"დაკავშირებულია."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"უკავშრდება."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"მდებარეობა <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"მაღვიძარა დაყენებულია: <xliff:g id="TIME">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"არეს დახურვა"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"მეტი დრო"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"ნაკლები დრო"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G ინტერნეტი გაითიშა."</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G მონაცემები გათიშულია"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"მობილური ინტერნეტი გაითიშა."</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> მოწყობილობა)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth გამორთულია"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"სიკაშკაშე"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"ავტო მობრუნება"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"როტაციის ჩაკეტვა"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"პორტრეტზე ჩაკეტილი"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ლანდშაფტზე ჩაკეტილი"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ავტოროტაცია"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"როტაცია ჩაკეტილია"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"პორტრეტი"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"პეიზაჟის რეჟიმი"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"შეყვანის მეთოდი"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"მდებარეობა"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"მდებარეობა გამორთულია"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"არ არის დაკავშირებული."</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ქსელი არ არის"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi გამორთულია"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast Screen"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"ეკრანის გადაცემა"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"განათება"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ავტომატურად"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"ფერების შებრუნება"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"დამატებითი პარამეტრები"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"მოდემის რეჟიმი"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"წვდომის წერტილი"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"ბოლო დროის"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"შეტყობინებები"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"ბოლო აპები არ არის"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"აპლიკაციის შესახებ"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"შესაძლოა ქსელზე\nმონიტორინგი ხორციელდებოდეს"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"შეეხეთ საჩვენებლად"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"არ შემაწუხოთ"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d სხვა"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"ქვემოთ მითითებულია ნაკლებად სასწრაფო შეტყობინებები"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"შეეხეთ ისევ გასახსნელად"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"გაასრიალეთ ზევით განსაბლოკად"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"გადაფურცლეთ მარჯვნივ ტელეფონისთვის"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"კამერისთვის მარცხენა შენაცვლება"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"სანამ ამას გამორთავდეთ"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>-ის შეცვლა დასრულებამდე)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"ერთი წუთით"</item>
+ <item quantity="other" msgid="6924190729213550991">"%d წუთით"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"ერთი საათით"</item>
+ <item quantity="other" msgid="5408537517529822157">"%d საათით"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index a9cf7d6..be41e50 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -64,7 +64,7 @@
<string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុងរក្សាទុករូបថតអេក្រង់…"</string>
<string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុងរក្សាទុករូបថតអេក្រង់..."</string>
<string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថតអេក្រង់កំពុងត្រូវបានរក្សាទុក។"</string>
- <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string>
+ <string name="screenshot_saved_title" msgid="6461865960961414961">"បានចាប់យករូបថតអេក្រង់។"</string>
<string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ដើម្បីមើលរូបថតអេក្រង់របស់អ្នក។"</string>
<string name="screenshot_failed_title" msgid="705781116746922771">"មិនអាចចាប់យករូបថតអេក្រង់។"</string>
<string name="screenshot_failed_text" msgid="8134011269572415402">"មិនអាចរក្សាទុករូបថតអេក្រង់។ ឧបករណ៍ផ្ទុកអាចកំពុងប្រើ។"</string>
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"បើក។"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"បិទ"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"បានតភ្ជាប់។"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"ការភ្ជាប់។"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -140,7 +141,7 @@
<string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាតការជូនដំណឹង។"</string>
<string name="accessibility_gps_enabled" msgid="3511469499240123019">"បានបើក GPS ។"</string>
<string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល GPS ។"</string>
- <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បានបើកម៉ាស៊ីនអង្គុលីលេខ"</string>
+ <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បានបើកម៉ាស៊ីនអង្គុលីលេខ"</string>
<string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធីរោទ៍ញ័រ។"</string>
<string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធីរោទ៍ស្ងាត់។"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
@@ -185,12 +186,12 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"ប៊្លូធូស (ឧបករណ៍ <xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"ប៊្លូធូសបានបិទ"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ពន្លឺ"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"បង្វិលស្វ័យប្រវត្តិ"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"បានចាក់សោការបង្វិល"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ចាក់សោបញ្ឈរ"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ចាក់សោផ្ដេក"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"បង្វិលស្វ័យប្រវត្តិ"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"បានចាក់សោការបង្វិល"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"បញ្ឈរ"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"ទេសភាព"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ត្របញ្ចូល"</string>
- <string name="quick_settings_location_label" msgid="5011327048748762257">"ទីតាំង"</string>
+ <string name="quick_settings_location_label" msgid="5011327048748762257">"ទីតាំង"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីតាំងបានបិទ"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណ៍មេឌៀ"</string>
<string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"មិនបានតភ្ជាប់"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"គ្មានបណ្ដាញ"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"វ៉ាយហ្វាយបានបិទ"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"ចាត់ថ្នាក់អេក្រង់"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"ចាត់ថ្នាក់អេក្រង់"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ពន្លឺ"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ស្វ័យប្រវត្តិ"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"ដាក់បញ្ច្រាសពណ៌"</string>
@@ -211,10 +212,10 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ការភ្ជាប់"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ហតស្ប៉ត"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ការជូនដំណឹង"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"ថ្មីៗ"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"មិនមានកម្មវិធីថ្មីៗ"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មានកម្មវិធី"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
- <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string>
+ <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញអាច\nត្រូវបានត្រួតពិនិត្យ"</string>
<string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string>
<string name="description_direction_up" msgid="7169032478259485180">"រុញឡើងលើដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
<string name="description_direction_left" msgid="7207478719805562165">"រុញទៅឆ្វេងដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"ប៉ះដើម្បីបង្ហាញ"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"កុំរំខាន"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d ទៀត"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"ការជូនដំណឹងមិនសូវបន្ទាន់ខាងក្រោម"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"ប៉ះម្ដងទៀត ដើម្បីបើក"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"អូសឡើងលើ ដើម្បីដោះសោ"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"អូសទៅស្ដាំដើម្បីបើកទូរស័ព្ទ"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"អូសទៅឆ្វេងដើម្បីប្រើម៉ាស៊ីនថត"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"រហូតដល់ពេលអ្នកបិទវា"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"កំពុងបញ្ចូលថ្ម (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ទើបពេញ)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"សម្រាប់មួយនាទី"</item>
<item quantity="other" msgid="6924190729213550991">"សម្រាប់ %d នាទី"</item>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index bb2e5c1..5fdc4e1 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"사용"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"사용 안함"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"연결됨"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"연결 중..."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"블루투스(<xliff:g id="NUMBER">%d</xliff:g>개의 기기)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"블루투스 사용 안함"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"밝기"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"자동 회전"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"회전 잠금"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"세로 모드로 고정됨"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"가로 모드로 고정됨"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"자동 회전"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"방향 고정"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"세로"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"가로"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"입력 방법"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"위치"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"위치 사용 중지"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"연결되어 있지 않음"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"네트워크가 연결되지 않음"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 꺼짐"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"화면 전송"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"화면 전송"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"밝기"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"자동"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"색상 반전"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"테더링"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"핫스팟"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"알림"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"최근"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"최근에 사용한 앱 없음"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"애플리케이션 정보"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"검색"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"네트워크가\n모니터링될 수 있음"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"표시하려면 터치"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"알림 일시중지"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d개 더보기"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"아래에 덜 급한 알림 표시"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"다시 탭하여 열기"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"위로 스와이프하여 잠금 해제"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"전화 기능을 사용하려면 오른쪽으로 스와이프하세요."</string>
+ <string name="camera_hint" msgid="5241441720959174226">"카메라를 사용하려면 왼쪽으로 스와이프하세요."</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"이 기능을 사용 중지할 때까지"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"충전 중(<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> 후 충전 완료)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1분 동안"</item>
<item quantity="other" msgid="6924190729213550991">"%d분 동안"</item>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 7220777..abcab9e 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"ເປີດ."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"ປິດ."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"ເຊື່ອມຕໍ່ແລ້ວ."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"ກຳລັງເຊື່ອມຕໍ່."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> ອຸປະກອນ)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth ປິດ"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ຄວາມສະຫວ່າງ"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"ໝຸນໜ້າຈໍອັດຕະໂນມັດ"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"ລັອກການປ່ຽນລວງ"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ລັອກເປັນຮູບລວງຕັ້ງ"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ລັອກເປັນຮູບລວງນອນ"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ໝຸນອັດຕະໂນມັດ"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"ລັອກການໝຸນຈໍແລ້ວ"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"ລວງຕັ້ງ"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"ລວງນອນ"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"ວິທີການປ້ອນຂໍ້ມູນ"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"ສະຖານທີ່"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"ຂໍ້ມູນສະຖານທີ່ປິດຢູ່"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ບໍ່ມີເຄືອຂ່າຍ"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ປິດ"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"ດຶງໜ້າຈໍ"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"ສົ່ງສັນຍານພາບ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ຄວາມແຈ້ງ"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"ອັດຕະໂນມັດ"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"ສະລັບສີ"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"ການປ່ອນສັນຍານ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ຮັອດສະປອດ"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"ການແຈ້ງເຕືອນ"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"ບໍ່ດົນມານີ້"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"ບໍ່ມີແອັບຯທີ່ຫາກໍໃຊ້"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ຂໍ້ມູນແອັບພລິເຄຊັນ"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ຊອກຫາ"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"ເຄືອຄ່າຍອາດ\nຖືກຕິດຕາມ"</string>
@@ -224,19 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"ແຕະເພື່ອສະແດງ"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"ຫ້າມລົບກວນ"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d ເພີ່ມເຕີມ"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"ການແຈ້ງເຕືອນທີ່ສຳຄັນໜ້ອຍກວ່າຢູ່ດ້ານລຸ່ມ"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"ແຕະອີກຄັ້ງເພື່ອເປີດ"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"ເລື່ອນຂຶ້ນເພື່ອປົດລັອກ"</string>
- <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າທ່ານຈະປິດ"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"ປັດຂວາເພື່ອໃຊ້ໂທລະສັບ"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"ປັດຊ້າຍເພື່ອໃຊ້ກ້ອງ"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າທ່ານຈະປິດ"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ກຳລັງສາກໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າຈະເຕັມ)"</string>
<plurals name="zen_mode_duration_minutes">
- <item quantity="one" msgid="9040808414992812341">"ສຳລັບນຶ່ງນາທີ"</item>
- <item quantity="other" msgid="6924190729213550991">"ສຳລັບ %d ນາທີ"</item>
+ <item quantity="one" msgid="9040808414992812341">"ເປັນເວລານຶ່ງນາທີ"</item>
+ <item quantity="other" msgid="6924190729213550991">"ເປັນເວລາ %d ນາທີ"</item>
</plurals>
<plurals name="zen_mode_duration_hours">
- <item quantity="one" msgid="3480040795582254384">"ສຳລັບນຶ່ງຊົ່ວໂມງ"</item>
- <item quantity="other" msgid="5408537517529822157">"ສຳລັບ %d ຊົ່ວໂມງ"</item>
+ <item quantity="one" msgid="3480040795582254384">"ເປັນເວລານຶ່ງຊົ່ວໂມງ"</item>
+ <item quantity="other" msgid="5408537517529822157">"ເປັນເວລາ %d ຊົ່ວໂມງ"</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index d0ebd54..4818a1f 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Įjungta."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Išjungta."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Prijungta."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Prisijungiama."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"„Bluetooth“ (<xliff:g id="NUMBER">%d</xliff:g> įreng.)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"„Bluetooth“ išjungta"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Šviesumas"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatiškai sukti"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Sukimas užrakintas"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Užrakinta stačia padėtis"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Užrakinta gulsčia padėtis"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatinis kaitaliojimas"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Kaitaliojimas užrakintas"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Stačias"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Gulsčias"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Įvesties metodas"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Vietovė"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Vietovė išjungta"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Neprisijungta"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tinklo nėra"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"„Wi-Fi“ išjungta"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Perduoti ekraną"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Perduoti ekraną"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Šviesumas"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATINIS"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Pakeisti spalvas"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Susiejimas"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Viešosios interneto prieigos taškas"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pranešimai"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"PASTARIEJI"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Nėra naujausių programų"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Programos informacija"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"paieška"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tinklas gali\nbūti stebimas"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Palieskite, kad būtų rodoma"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Netrukdyti"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Dar %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Mažiau skubūs pranešimai toliau"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Palieskite dar kartą, kad atidarytumėte"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Perbraukite aukštyn, kad atrakintumėte"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Perbraukite į dešinę, kad galėtumėte skambinti"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Perbraukite į kairę, kad būtų įjungtas fotoaparatas"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Kol išjungsite"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Kraunama (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> iki visiško įkrovimo)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 min."</item>
<item quantity="other" msgid="6924190729213550991">"%d min."</item>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 23ce4a9..5a3a563 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Ieslēgts"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Izslēgts"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Savienojums ir izveidots."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Notiek savienojuma izveide..."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> ierīce(-es))"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth savienojums ir izslēgts."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Spilgtums"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automātiska pagriešana"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Pagriešana bloķēta"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Fiksēts portreta režīmā"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Fiksēts ainavas režīmā"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automātiska pagriešana"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Pagriešana bloķēta"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portrets"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Ainava"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Ievades metode"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Atrašanās vieta"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Atrašanās vieta izslēgta"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nav izveidots savienojums"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nav tīkla"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ir izslēgts"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Apraides ekrāns"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Apraides ekrāns"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Spilgtums"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMĀTISKI"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertēt krāsas"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Piesaiste"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Tīklājs"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Paziņojumi"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"JAUNĀKIE"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Nav nesen izmantotu lietotņu"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informācija par lietojumprogrammu"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"Meklēt"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Tīkls var\ntikt uzraudzīts"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Pieskarieties, lai rādītu"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Netraucēt"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"vēl %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Mazāk steidzami paziņojumi tiek rādīti tālāk"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Pieskarieties vēlreiz, lai atvērtu"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Velciet uz augšu, lai atbloķētu"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Lai lietotu tālruni, velciet pa labi."</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Lai lietotu kameru, velciet pa kreisi."</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Līdz brīdim, kad izslēgsiet"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Notiek uzlāde (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> līdz pilnīgai uzlādei)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Vienu minūti"</item>
<item quantity="other" msgid="6924190729213550991">"%d min"</item>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index c00b7ed..9c02521 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Идэвхижсэн."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Унтраах"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Холбогдсон."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Холбож байна."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Блютүүт <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Байршил <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Сэрүүлгийг <xliff:g id="TIME">%s</xliff:g>-д тохируулсан."</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Самбарыг хаах"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Цаг нэмэх"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Цаг хасах"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G дата идэвхгүй болов"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G дата идэвхгүй байна"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Мобайл дата идэвхгүй болов"</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Блютүүт (<xliff:g id="NUMBER">%d</xliff:g> төхөөрөмж)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Блютүүт унтраалттай"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Тодрол"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Автомат эргэх"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Эргүүлэлт түгжигдсэн"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Босоо байдлаар түгжсэн"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Хэвтээ байдлаар түгжсэн"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматаар эргэх"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Эргэлтийг түгжсэн"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Босоо"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Хэвтээ"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Оруулах арга"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Байршил"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Байршил идэвхгүй"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Холбогдоогүй"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Сүлжээгүй"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi унтарсан"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Дамжуулах дэлгэц"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Дамжуулах дэлгэц"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Тодрол"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТОМАТ"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Өнгийг урвуулах"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Өөр тохиргоо"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Модем болгох"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Сүлжээний цэг"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"СҮҮЛИЙН"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Мэдэгдэл"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Сүүлд ашигласан апп байхгүй"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Аппликешны мэдээлэл"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"хайх"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Сүлжээ хянагдаж\nбайж болзошгүй"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Харуулах бол хүрнэ үү"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Бүү саад бол"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"өөр %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Яаралтай биш мэдэгдлүүдийг доор"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Нээхийн тулд дахин товшино уу"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Түгжээг тайлах бол шудрана уу"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Утас гаргахын тулд баруун шударна уу"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Камер гаргахын тулд зүүн шударна уу"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Таныг унтраах хүртэл"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Цэнэглэж байна (дүүргэхэд <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Нэг минутын турш"</item>
+ <item quantity="other" msgid="6924190729213550991">"%d минутын турш"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Нэг цагийн турш"</item>
+ <item quantity="other" msgid="5408537517529822157">"%d цагийн турш"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 263fc46..7ff19ba 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Dihidupkan."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Dimatikan."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Disambungkan."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Menyambung."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokasi <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Penggera ditetapkan pada <xliff:g id="TIME">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Tutup panel"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Lagi masa"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kurang masa"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data 2G-3G dilumpuhkan"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data 4G dilumpuhkan"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Data mudah alih dilumpuhkan"</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Peranti)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth Dimatikan"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Kecerahan"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Auto Putar"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Putaran Dikunci"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Dikunci kepada Potret"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Dikunci kepada Landskap"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Autoputar"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Putaran dikunci"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Potret"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Landskap"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Kaedah Input"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Lokasi"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Lokasi Dimatikan"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Tidak Disambungkan"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tiada Rangkaian"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Dimatikan"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Skrin Cast"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Skrin Cast"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Kecerahan"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Terbalikkan warna"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Lagi tetapan"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Penambatan"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Tempat liputan"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"TERBAHARU"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Pemberitahuan"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Tiada apl terbaharu"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Maklumat Aplikasi"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Rangkaian mungkin\nboleh dipantau"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Sentuh untuk menunjukkan"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Jangan ganggu"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d lagi"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Pemberitahuan kurang penting di bawah"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Ketik lagi untuk membuka"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Leret ke atas untuk membuka kunci"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Leret ke kanan untuk telefon"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Leret ke kiri untuk kamera"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Sehingga anda matikan"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengecas (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Selama satu minit"</item>
+ <item quantity="other" msgid="6924190729213550991">"Selama %d minit"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Selama satu jam"</item>
+ <item quantity="other" msgid="5408537517529822157">"Selama %d jam"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 75706a2..f0316fc 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"På."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Av."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Tilkoblet."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Kobler til."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> enheter)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth er slått av"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Lysstyrke"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatisk rotasjon"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotasjon er låst"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Låst til stående format"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Låst til liggende format"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotér automatisk"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotasjonen er låst"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portrett"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Landskap"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Inndatametode"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Sted"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Posisjon av"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ikke tilkoblet"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ingen nettverk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi er av"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast skjermen"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Cast skjermen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter farger"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tilknytning"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Wi-Fi-sone"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Varsler"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"NYLIGE"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Ingen nylige apper"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformasjon"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Nettverket kan\nvære overvåket"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Trykk for å vise"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Ikke forstyrr"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d til"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre presserende varsler nedenfor"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Trykk på nytt for å åpne"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Sveip oppover for å låse opp"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Sveip mot høyre for å åpne telefonen"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Sveip mot venstre for å åpne kameraet"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Inntil du slår av funksjonen"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Lader (fulladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"I ett minutt"</item>
<item quantity="other" msgid="6924190729213550991">"I %d minutter"</item>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index c4bef68..198eb8e 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Ingeschakeld."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Uitgeschakeld."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Verbonden."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Verbinden."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> apparaten)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth uit"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Helderheid"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatische rotatie"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotatie vergrendeld"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Vergrendeld in staande stand"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Vergrendeld in liggende stand"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatische rotatie"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotatie vergrendeld"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portret"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Landschap"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Invoermethode"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Locatie"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Locatie uit"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Niet verbonden"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Geen netwerk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wifi uit"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Scherm casten"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Scherm casten"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Helderheid"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATISCH"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Kleuren omkeren"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Meldingen"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Geen recente apps"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"App-informatie"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk kan\nworden gecontroleerd"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Raak aan om weer te geven"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Niet storen"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Nog %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Minder urgente meldingen onderaan"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tik nogmaals om te openen"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Veeg omhoog om te ontgrendelen"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Veeg naar rechts voor telefoon"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Veeg naar links voor camera"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Totdat u dit uitschakelt"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Opladen (vol over <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Eén minuut"</item>
<item quantity="other" msgid="6924190729213550991">"%d minuten"</item>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 825dfec..a83c07d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Wł."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Wył."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Połączono."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Łączę..."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (urządzenia: <xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth wył."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Jasność"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Autoobracanie"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Obracanie jest zablokowane"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Zablokowano w orientacji pionowej"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Zablokowano w orientacji poziomej"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Autoobracanie"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Obracanie zablokowane"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Pionowo"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Poziomo"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Metoda wprowadzania"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Lokalizacja"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Lokalizacja wyłączona"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Brak połączenia"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Brak sieci"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wyłącz Wi-Fi"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Ekran Cast"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Ekran Cast"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jasność"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATYCZNA"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Odwróć kolory"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Powiązanie"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Punkt dostępu"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Powiadomienia"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"OSTATNIE"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Brak ostatnio uruchomionych aplikacji"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informacje o aplikacji"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Sieć może być\nmonitorowana"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Dotknij, by zobaczyć"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Nie przeszkadzać"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d więcej"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Poniżej widać mniej pilne powiadomienia"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Kliknij ponownie, by otworzyć"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Przesuń w górę, by odblokować"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Przesuń w prawo, by przełączyć się na telefon"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Przesuń w lewo, by przełączyć się na aparat"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dopóki nie wyłączysz"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ładuje się (pełne naładowanie za <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Przez minutę"</item>
<item quantity="other" msgid="6924190729213550991">"Przez %d min"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index f9ac828..560f64a 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Ativado."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Desativado."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Ligado."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"A ligar..."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Dispositivos)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth desat."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rodar automat."</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotação Bloqueada"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Bloqueado em retrato"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Bloqueado em paisagem"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotação automática"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotação bloqueada"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Vertical"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Horizontal"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de Introdução"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Localização"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localização Desativada"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Não Ligado"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sem Rede"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Desligado"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Transmitir ecrã"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Transmitir ecrã"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMÁTICO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Associação"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Nenhuma aplicação recente"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações da aplicação"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"A rede pode ser\nmonitorizada"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Toque para mostrar"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Não incomodar"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Mais %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notificações menos urgentes abaixo"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Deslizar rapidamente com o dedo para cima para desbloquear"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Deslize rapidamente para a direita para aceder ao telemóvel"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Deslize rapidamente para a esquerda para aceder à câmara"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Até que o utilizador desative"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"A carregar (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até à carga máxima)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Durante um minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Durante %d minutos"</item>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 1c13e62..9dcb25da 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Ligado."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Desligado."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Conectado."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Conectando."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,12 +159,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Localização <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarme definido para <xliff:g id="TIME">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Fechar painel"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mais tempo"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Menos tempo"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dados 2G e 3G desativados"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dados 4G desativados"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Dados móveis desativados"</string>
@@ -190,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> dispositivos)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth desativado"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brilho"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Girar automat."</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotação bloqueada"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Bloqueado no modo retrato"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Bloqueado no modo paisagem"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotação automática"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotação bloqueada"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Retrato"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Paisagem"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Método de entrada"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Localização"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localização desativada"</string>
@@ -207,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Não conectado"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sem rede"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desligado"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Transmitir tela"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Transmitir tela"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brilho"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inverter cores"</string>
@@ -215,9 +213,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Mais configurações"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Ponto de acesso"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTES"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificações"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Nenhum app recente"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informações do aplicativo"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"A rede pode estar\nsob monitoração"</string>
@@ -230,16 +227,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Toque para mostrar"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Não perturbe"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Mais %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notificações menos urgentes abaixo"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Toque novamente para abrir"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Deslize para cima para desbloquear"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Deslize para a esquerda para usar o telefone"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Deslize para a esquerda para usar a câmera"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Até você desativar"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Carregando (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> até concluir)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Por 1 minuto"</item>
+ <item quantity="other" msgid="6924190729213550991">"Por %d minutos"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Por 1 hora"</item>
+ <item quantity="other" msgid="5408537517529822157">"Por %d horas"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-rm/strings.xml b/packages/SystemUI/res/values-rm/strings.xml
index 00bf8c0..73c0cd2 100644
--- a/packages/SystemUI/res/values-rm/strings.xml
+++ b/packages/SystemUI/res/values-rm/strings.xml
@@ -210,6 +210,8 @@
<skip />
<!-- no translation found for accessibility_desc_connected (8366256693719499665) -->
<skip />
+ <!-- no translation found for accessibility_desc_connecting (3812924520316280149) -->
+ <skip />
<!-- no translation found for accessibility_data_connection_gprs (1606477224486747751) -->
<skip />
<!-- no translation found for accessibility_data_connection_1x (994133468120244018) -->
@@ -340,13 +342,13 @@
<skip />
<!-- no translation found for quick_settings_brightness_label (6968372297018755815) -->
<skip />
- <!-- no translation found for quick_settings_rotation_unlocked_label (336054930362580584) -->
+ <!-- no translation found for quick_settings_rotation_unlocked_label (7305323031808150099) -->
<skip />
- <!-- no translation found for quick_settings_rotation_locked_label (8058646447242565486) -->
+ <!-- no translation found for quick_settings_rotation_locked_label (6359205706154282377) -->
<skip />
- <!-- no translation found for quick_settings_rotation_locked_portrait_label (1553131290066230775) -->
+ <!-- no translation found for quick_settings_rotation_locked_portrait_label (5102691921442135053) -->
<skip />
- <!-- no translation found for quick_settings_rotation_locked_landscape_label (7216265671276086593) -->
+ <!-- no translation found for quick_settings_rotation_locked_landscape_label (8553157770061178719) -->
<skip />
<!-- no translation found for quick_settings_ime_label (7073463064369468429) -->
<skip />
@@ -374,7 +376,7 @@
<skip />
<!-- no translation found for quick_settings_wifi_off_label (7558778100843885864) -->
<skip />
- <!-- no translation found for quick_settings_remote_display_no_connection_label (372107699274391290) -->
+ <!-- no translation found for quick_settings_remote_display_no_connection_label (7482103121002965053) -->
<skip />
<!-- no translation found for quick_settings_brightness_dialog_title (8599674057673605368) -->
<skip />
@@ -392,7 +394,7 @@
<skip />
<!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
<skip />
- <!-- no translation found for recents_empty_message (2269156590813544104) -->
+ <!-- no translation found for recents_empty_message (7883614615463619450) -->
<skip />
<!-- no translation found for recents_app_info_button_label (2890317189376000030) -->
<skip />
@@ -412,15 +414,22 @@
<skip />
<!-- no translation found for zen_mode_title (8793432092004749188) -->
<skip />
- <!-- no translation found for keyguard_more_overflow_text:other (9180696159506883684) -->
+ <!-- no translation found for keyguard_more_overflow_text (9195222469041601365) -->
+ <skip />
<!-- no translation found for speed_bump_explanation (1288875699658819755) -->
<skip />
<!-- no translation found for notification_tap_again (7590196980943943842) -->
<skip />
<!-- no translation found for keyguard_unlock (8043466894212841998) -->
<skip />
+ <!-- no translation found for phone_hint (3101468054914424646) -->
+ <skip />
+ <!-- no translation found for camera_hint (5241441720959174226) -->
+ <skip />
<!-- no translation found for zen_mode_forever (7420011936770086993) -->
<skip />
+ <!-- no translation found for keyguard_indication_charging_time (1757251776872835768) -->
+ <skip />
<!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
<!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
<!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 7ffa77c..6f992e4 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Activat."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Dezactivat."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Conectat."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Se conectează."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Locație: <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarmă setată pentru <xliff:g id="TIME">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Închideți panoul"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Mai mult timp"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mai puțin timp"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Datele 2G-3G au fost dezactivate"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Datele 4G au fost dezactivate"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Datele mobile au fost dezactivate"</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> dispozitive)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth dezactivat"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Luminozitate"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotire automată"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotire blocată"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Blocat la afișarea „portret”"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Blocat la afișarea „peisaj”"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotire automată"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotire blocată"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portret"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Peisaj"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Metodă de introducere"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Locație"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localizarea este dezactivată"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Neconectat"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nicio reţea"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi deconectat"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Ecran de afișare a transmisiunii"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Ecran de trimitere"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Luminozitate"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMAT"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Inversați culori"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Mai multe setări"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificări"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Nicio aplicație recentă"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informații despre aplicație"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Rețeaua poate\nfi monitorizată"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Atingeți pentru a afișa"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Nu deranjaţi"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Încă %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Notificările mai puțin urgente mai jos"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Atingeți din nou pentru a deschide"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Glisați în sus pentru a debloca"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Glisați la dreapta pentru a acesa telefonul"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Glisați la stânga pentru a accesa camera foto"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Până la dezactivare"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Se încarcă (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Timp de un minut"</item>
+ <item quantity="other" msgid="6924190729213550991">"Timp de %d (de) minute"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Timp de o oră"</item>
+ <item quantity="other" msgid="5408537517529822157">"Timp de %d (de) ore"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index d726428..12232c5 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Вкл."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Выкл."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Подключено"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Соединение."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth выкл."</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Яркость"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Автоповорот"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Автоповорот выкл."</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Только вертикально"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Только горизонтально"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоповорот"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Автоповорот отключен"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Вертикальная ориентация"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Горизонтальная ориентация"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Способ ввода"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Передача геоданных"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Местоположение выкл."</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Нет соединения"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нет сети"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi выкл."</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Wi-Fi-монитор"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Беспроводной монитор"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яркость"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТОНАСТРОЙКА"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Инвертировать"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Режим модема"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка доступа"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Уведомления"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"НЕДАВНИЕ СООБЩЕНИЯ"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Ничего не найдено."</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Сведения о приложении"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"поиск"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Действия в сети\nмогут отслеживаться"</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Нажмите, чтобы открыть"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Не беспокоить"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Ещё %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Показать менее важные оповещения"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Нажмите ещё раз, чтобы открыть"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Для разблокировки проведите пальцем по экрану"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Чтобы открыть приложение \"Телефон\", пролистните вправо"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Чтобы включить камеру, пролистните влево"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Пока я не отключу"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Зарядка батареи (осталось <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 мин."</item>
<item quantity="other" msgid="6924190729213550991">"%d мин."</item>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 8debc9a..e5e0802 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Zapnuté."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Vypnuté."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Pripojené."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Pripája sa"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,12 +159,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth: <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Poloha: <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Budík nastavený na <xliff:g id="TIME">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Zavrieť panel"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Dlhší čas"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Kratší čas"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Dátové prenosy 2G a 3G sú zakázané"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Dátové prenosy 4G sú zakázané"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobilné dátové prenosy sú zakázané"</string>
@@ -190,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Rozhranie Bluetooth (počet zariadení: <xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Rozhranie Bluetooth je vypnuté"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Jas"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatické otáčanie"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Otáčanie uzamknuté"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Uzamknuté na výšku"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Uzamknuté na šírku"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Automatické otáčanie"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Otáčanie je uzamknuté"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Na výšku"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Na šírku"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Metóda vstupu"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Poloha"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Poloha vypnutá"</string>
@@ -207,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Nepripojené"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Žiadna sieť"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Sieť Wi-Fi je vypnutá"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Vzdialená obrazovka"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Vzdialená obrazovka"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Jas"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATICKY"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertovať farby"</string>
@@ -215,9 +213,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Ďalšie nastavenia"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Zdieľanie dátového pripojenia"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"NEDÁVNE"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Upozornenia"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Žiadne nedávne aplikácie"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Informácie o aplikácii"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"hľadať"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Sieť môže byť\nmonitorovaná"</string>
@@ -230,16 +227,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Upozornenie zobrazíte dotykom"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Nerušiť"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d ďalších"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Menej naliehavé upozornenia sa nachádzajú nižšie"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Upozornenie otvoríte opätovným klepnutím"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Zariadenie odomknete prejdením prstom nahor"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Telefón otvoríte prejdením prstom doľava"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Fotoaparát otvoríte prejdením prstom doľava"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Dokým túto funkciu nevypnete"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nabíja sa (úplné nabitie o <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Na jednu minútu"</item>
+ <item quantity="other" msgid="6924190729213550991">"Na %d min"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Na jednu hodinu"</item>
+ <item quantity="other" msgid="5408537517529822157">"Na %d h"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 6171c16..8fbd70d 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Vklopljen."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Izklopljen."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Povezan."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Povezovanje."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (št. naprav: <xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth izklopljen"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Svetlost"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Samodejno vrtenje"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Zaklenjeno vrtenje"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Zaklenjeno na pokončno postavitev"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Zaklenjeno na ležečo postavitev"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Samodejno sukanje"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Sukanje je zaklenjeno"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Pokončno"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Ležeče"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Način vnosa"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Lokacija"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Lokacija izklopljena"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Povezava ni vzpostavljena"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ni omrežja"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi izklopljen"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Zaslon za predvajanje"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Zaslon za predvajanje"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Svetlost"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"SAMODEJNO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Obrni barve"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Internet prek mobilne naprave"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Dostopna točka"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obvestila"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"NEDAVNI"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Ni nedavnih aplikacij"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Podatki o aplikaciji"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Omrežje je\nlahko spremljano"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Dotaknite se za prikaz"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Ne moti"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"še %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Manj nujna obvestila spodaj"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Znova se dotaknite, da odprete"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Povlecite, da odklenete"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Povlecite v desno za telefon"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Povlecite v levo za fotoaparat"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Dokler tega ne izklopite"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Polnjenje (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> do napolnjenosti)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Za eno minuto"</item>
<item quantity="other" msgid="6924190729213550991">"Za %d min"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 14782e6..db8e278 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Укључено."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Искључено."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Повезано је."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Повезивање."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> уређаја)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth искључен"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Осветљеност"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Аутоматско ротирање"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Ротирање је закључано"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Закључано на Усправно"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Закључано на Водоравно"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Аутоматска ротација"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Ротација је закључана"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Вертикални приказ"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Хоризонтални приказ"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Метод уноса"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Локација"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Локација је искључена"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Веза није успостављена"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нема мреже"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi је искључен"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Пребаци екран"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Пребацивање екрана"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Осветљеност"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АУТОМАТСКА"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Обрни боје"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Повезивање"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Хотспот"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Обавештења"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"НАЈНОВИЈЕ"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Нема недавних апликација"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Информације о апликацији"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"претражи"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Мрежа се можда\nнадгледа"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Додирните за приказ"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Не узнемиравај"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Још %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Мање хитна обавештења су у наставку"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Додирните поново да бисте отворили"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Превуците нагоре да бисте откључали"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Превуците удесно за телефон"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Превуците улево за камеру"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Док не искључите"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Пуњење (пун је за <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Један минут"</item>
<item quantity="other" msgid="6924190729213550991">"%d мин"</item>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index a7fb15d..f92a4f7 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Aktiverad."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Inaktiverad."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Ansluten."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Ansluter."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> enheter)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth av"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Ljusstyrka"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Rotera automatiskt"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Rotationen har låsts"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Låst i stående läge"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Låst i liggande läge"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Rotera automatiskt"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Rotationen har låsts"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Stående"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Liggande"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Inmatningsmetod"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Plats"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Plats har inaktiverats"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ej ansluten"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Inget nätverk"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi av"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Överför skärmen"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Casta skärmen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ljusstyrka"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Invertera färger"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Internetdelning"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Trådlös surfzon"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Aviseringar"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"NYA"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Inga aktiva appar"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Appinformation"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Nätverket kan\nvara övervakat"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Tryck här om du vill visa aviseringar"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Stör ej"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d till"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Mindre brådskande aviseringar nedan"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Tryck igen för att öppna"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Dra uppåt om du vill låsa upp"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Dra åt höger om du vill visa telefonen"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Dra åt vänster om du vill visa kameran"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Tills du inaktiverar detta"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Laddar (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> tills batteriet är fulladdat)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"I en minut"</item>
<item quantity="other" msgid="6924190729213550991">"I %d minuter"</item>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index b8d8c58..24cd574 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -118,6 +118,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Imewashwa."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Imezimwa."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Imeunganishwa."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Inaunganisha."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -154,12 +155,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Mahali <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Kengele imewekwa <xliff:g id="TIME">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Funga paneli ya maelezo"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Muda zaidi"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Muda kidogo"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Data ya 2G-3G imelemazwa"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Data ya 4G imelemazwa"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Data ya kifaa cha mkononi imelemazwa"</string>
@@ -186,10 +184,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (Vifaa <xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth Imezimwa"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Ung\'avu"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Zungusha Otomatiki"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Mzunguko Umefungwa"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Imefungwa katika mkamo Wima"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Imefungwa katika mkao Mlalo"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Zungusha kiotomatiki"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Mzunguko umefungwa"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Wima"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Mlalo"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Mbinu ya uingizaji"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Kutambua Eneo"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Kitambua eneo kimezimwa"</string>
@@ -203,7 +201,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Haijaunganishwa"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Hakuna Mtandao"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Imezimwa"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Utumaji wa Skrini"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Utumaji wa Skrini"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ung\'avu"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"KIOTOMATIKI"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Pindua rangi"</string>
@@ -211,9 +209,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Mipangilio zaidi"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Kusambaza mtandao"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Mtandao-hewa"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"YA HIVI KARIBUNI"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Arifa"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Hakuna programu za karibuni"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Maelezo ya Programu"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"tafuta"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Huenda mtandao\nunafuatiliwa"</string>
@@ -226,16 +223,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Gusa ili zionekane"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Usisumbue"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d zaidi"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>+"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Arifa zisizo za dharura sana ziko hapo chini"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Gonga tena ili ufungue"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Telezesha kidole ili ufungue"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Telezesha kidole kulia ili ufikie simu"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Telezesha kidole kushoto ili ufikie kamera"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Hadi utakapozima hili"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Inachaji ( <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hadi ijae)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Kwa dakika moja"</item>
+ <item quantity="other" msgid="6924190729213550991">"Kwa dakika %d"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Kwa saa moja"</item>
+ <item quantity="other" msgid="5408537517529822157">"Kwa saa %d"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index dc75e78..0d6bb5c 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"เปิดอยู่"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"ปิดอยู่"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"เชื่อมต่อแล้ว"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"กำลังเชื่อมต่อ"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"บลูทูธ <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"สถานที่ <xliff:g id="STATE">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ตั้งเวลาปลุกไว้ที่ <xliff:g id="TIME">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"ปิดแผงควบคุม"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"เวลามากขึ้น"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"เวลาน้อยลง"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"ปิดใช้งานข้อมูล 2G-3G แล้ว"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"ปิดใช้งานข้อมูล 4G แล้ว"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"ปิดใช้งานข้อมูลมือถือแล้ว"</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"บลูทูธ (<xliff:g id="NUMBER">%d</xliff:g> อุปกรณ์)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"ปิดบลูทูธ"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"ความสว่าง"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"หมุนอัตโนมัติ"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"ล็อกการหมุนแล้ว"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ล็อกที่แนวตั้ง"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ล็อกที่แนวนอน"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"หมุนอัตโนมัติ"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"ล็อกการหมุน"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"บุคคล"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"แนวนอน"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"วิธีป้อนข้อมูล"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"ตำแหน่ง"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"ปิดตำแหน่ง"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"ไม่ได้เชื่อมต่อ"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ไม่มีเครือข่าย"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"ปิด WiFi"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"ส่งหน้าจอ"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"ส่งหน้าจอ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"ความสว่าง"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"อัตโนมัติ"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"สลับสี"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"การตั้งค่าเพิ่มเติม"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"การปล่อยสัญญาณ"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ฮอตสปอต"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"ล่าสุด"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"การแจ้งเตือน"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"ไม่มีแอปล่าสุด"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"ข้อมูลแอปพลิเคชัน"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ค้นหา"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"เครือข่ายอาจ\nถูกตรวจสอบ"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"แตะเพื่อแสดง"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"ห้ามรบกวน"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"อีก %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"การแจ้งเตือนที่เร่งด่วนน้อยด้านล่าง"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"แตะอีกครั้งเพื่อเปิด"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"กวาดขึ้นเพื่อปลดล็อก"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"กวาดไปทางขวาเพื่อใช้โทรศัพท์"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"กวาดไปทางซ้ายเพื่อใช้กล้องถ่ายรูป"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"จนกว่าคุณจะปิดฟังก์ชันนี้"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"กำลังชาร์จ (อีก <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> เต็ม)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"1 นาที"</item>
+ <item quantity="other" msgid="6924190729213550991">"%d นาที"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"1 ชั่วโมง"</item>
+ <item quantity="other" msgid="5408537517529822157">"%d ชั่วโมง"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 0713ddd..a76eb5f 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Naka-on."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Naka-off."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Nakakonekta."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Kumokonekta."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"<xliff:g id="STATE">%s</xliff:g> ng Bluetooth."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Lokasyon <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm set para sa <xliff:g id="TIME">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Isara ang panel"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Higit pang oras"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Mas kaunting oras"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"Di pinapagana ang 2G-3G na data"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"Hindi pinapagana ang 4G na data"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Hindi pinapagana ang data ng mobile"</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> (na) Device)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Naka-off ang Bluetooth"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Brightness"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"I-auto Rotate"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Naka-lock ang Pag-rotate"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Naka-lock sa Portrait"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Naka-lock sa Landscape"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Awtomatikong i-rotate"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Naka-lock ang pag-ikot"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Portrait"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Landscape"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Pamamaraan ng Pag-input"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Lokasyon"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Naka-off ang Lokasyon"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Hindi Nakakonekta"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Walang Network"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Naka-off ang Wi-Fi"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"I-cast ang Screen"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"I-cast ang screen"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Brightness"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"I-invert ang mga kulay"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Marami pang setting"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Nagte-tether"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"MGA KAMAKAILAN"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Mga Notification"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Walang kamakailang mga app"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Impormasyon ng Application"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Maaaring\nsinusubaybayan ang network"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Pindutin upang ipakita"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Huwag istorbohin"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d pa"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Nasa ibaba ang mga notification na hindi masyadong mahalaga"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"I-tap ulit upang buksan"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Mag-swipe pataas upang i-unlock"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Mag-swipe pakanan para sa telepono"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Mag-swipe pakaliwa para sa camera"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Hanggang sa i-off mo ito"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Nagtsa-charge (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> hanggang mapuno)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Sa loob ng isang minuto"</item>
+ <item quantity="other" msgid="6924190729213550991">"Sa loob ng %d (na) minuto"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Sa loob ng isang oras"</item>
+ <item quantity="other" msgid="5408537517529822157">"Sa loob ng %d (na) oras"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index c0ba250..8b37f15 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Açık."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Kapalı."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Bağlandı."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Bağlanıyor."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -156,12 +157,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"Bluetooth <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"Konum: <xliff:g id="STATE">%s</xliff:g>."</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"Alarm saati: <xliff:g id="TIME">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"Paneli kapatın"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"Daha uzun süre"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"Daha kısa süre"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G verileri devre dışı bırakıldı"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G verileri devre dışı"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"Mobil veriler devre dışı"</string>
@@ -188,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> Cihaz)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth Kapalı"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Parlaklık"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Otomatik Döndür"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Dönme Kilitlendi"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Dikey Görünüme Kilitlendi"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Yatay Görünüme Kilitlendi"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Otomatik döndür"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Döndürme kilitlendi"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Dikey"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Yatay"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Giriş Yöntemi"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Konum"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Konum Bilgisi Kapalı"</string>
@@ -205,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Bağlı Değil"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ağ yok"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Kablosuz Kapalı"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Yayınlama Ekranı"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Ekranı yayınla"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Parlaklık"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OTOMATİK"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Renkleri çevir"</string>
@@ -213,9 +211,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"Diğer ayarlar"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"SON İLETİLER"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Bildirimler"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Yakın zamanda kullanılan uygulama yok"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Uygulama Bilgileri"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Ağ izleniyor\nolabilir"</string>
@@ -228,16 +225,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Görüntülemek için dokunun"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Rahatsız etmeyin"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d adet daha"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Daha az acil bildirimler aşağıdadır"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Açmak için tekrar hafifçe vurun"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Kilidi açmak için hızlıca yukarı kaydırın"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"Telefon için sağa kaydırın"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Kamera için sola kaydırın"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"Siz bunu kapatana kadar"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Şarj oluyor (tamamen dolmasına <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kaldı)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"Bir dakika süreyle"</item>
+ <item quantity="other" msgid="6924190729213550991">"%d dakika süreyle"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"Bir saat süreyle"</item>
+ <item quantity="other" msgid="5408537517529822157">"%d saat süreyle"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index e149d7b..2d3609f 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Увімкнено."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Вимкнено."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Під’єднано."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"З’єднання."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (пристроїв: <xliff:g id="NUMBER">%d</xliff:g>)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Bluetooth вимкнено"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Яскравість"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Обертати автоматично"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Обертання заблоковано"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Заблоковано в книжковій орієнтації"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Заблоковано в альбомній орієнтації"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Автоматичне обертання"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Обертання заблоковано"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Книжкова орієнтація"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Альбомна орієнтація"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Метод введення"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Місцезнаходження"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Місцезнаходження вимкнено"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Не під’єднано."</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Немає мережі"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi вимкнено"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Транслювати екран"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Транслювати екран"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Яскравість"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"АВТО"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Інвертувати кольори"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Режим модема"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Точка доступу"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Сповіщення"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"ОСТАННІ"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Немає останніх додатків"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Інформація про додаток"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Мережа може\nвідстежуватися"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Торкніться, щоб показати"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Не турбувати"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"Ще %d"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Менше термінових сповіщень нижче"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Торкніться знову, щоб відкрити"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Проведіть пальцем угору, щоб розблокувати"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Проведіть пальцем праворуч, щоб скористатися телефоном"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Проведіть пальцем ліворуч, щоб скористатися камерою"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Доки ви не вимкнете"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Заряджання (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> до повного зарядження)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Протягом хвилини"</item>
<item quantity="other" msgid="6924190729213550991">"Протягом %d хв"</item>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index f99c341..e15b1b5 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Bật."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Tắt."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Đã kết nối."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Đang kết nối."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> thiết bị)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"Đã tắt Bluetooth"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Độ sáng"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Tự động xoay"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Khóa xoay"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Đã khóa ở chế độ xoay dọc"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Đã khóa ở chế độ xoay ngang"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Tự động xoay"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Khóa xoay"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Dọc"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Ngang"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Phương thức nhập"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Vị trí"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Tắt vị trí"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Chưa được kết nối"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Không có mạng nào"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Tắt Wi-Fi"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Truyền màn hình"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Màn hình truyền"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Độ sáng"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"TỰ ĐỘNG"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Đảo ngược màu"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Đang dùng làm điểm truy cập Internet"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Điểm phát sóng"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Thông báo"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"GẦN ĐÂY"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Không có ứng dụng nào gần đây"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Thông tin ứng dụng"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Mạng có thể\nđược giám sát"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Chạm để hiển thị"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Không làm phiền"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d thông báo khác"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Thông báo ít khẩn cấp hơn bên dưới"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Nhấn lại để mở"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Vuốt lên để mở khóa"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Vuốt sang phải để mở điện thoại"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Vuốt sang trái để mở máy ảnh"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Cho đến khi bạn tắt tính năng này"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Đang sạc (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> cho đến khi đầy)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Trong một phút"</item>
<item quantity="other" msgid="6924190729213550991">"Trong %d phút"</item>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 6940b2c..2bcd6b8 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"开启。"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"关闭。"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"已连接。"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"正在连接。"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -158,12 +159,9 @@
<string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"蓝牙:<xliff:g id="STATE">%s</xliff:g>。"</string>
<string name="accessibility_quick_settings_location" msgid="4577282329866813100">"位置信息服务<xliff:g id="STATE">%s</xliff:g>。"</string>
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"闹钟已设置为:<xliff:g id="TIME">%s</xliff:g>。"</string>
- <!-- no translation found for accessibility_quick_settings_close (2571790856136835943) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_more_time (5778794273488176726) -->
- <skip />
- <!-- no translation found for accessibility_quick_settings_less_time (101026945195230084) -->
- <skip />
+ <string name="accessibility_quick_settings_close" msgid="2571790856136835943">"关闭面板"</string>
+ <string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更长时间"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"更短时间"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"2G-3G 数据网络已停用"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"4G 数据网络已停用"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"移动数据网络已停用"</string>
@@ -190,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"蓝牙(<xliff:g id="NUMBER">%d</xliff:g> 台设备)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"蓝牙:关闭"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"亮度"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"自动旋转"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"屏幕方向:锁定"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"锁定为纵向"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"锁定为横向"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自动旋转"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"旋转功能已锁定"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"纵向"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"横向"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"输入法"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"位置信息"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"位置信息:关闭"</string>
@@ -207,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未连接"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"无网络"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WLAN:关闭"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"投射屏幕"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"投射屏幕"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自动"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"反色"</string>
@@ -215,9 +213,8 @@
<string name="quick_settings_more_settings" msgid="326112621462813682">"更多设置"</string>
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"网络共享"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"热点"</string>
- <!-- no translation found for quick_settings_notifications_label (4818156442169154523) -->
- <skip />
- <string name="recents_empty_message" msgid="2269156590813544104">"最近"</string>
+ <string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"最近没有用过任何应用"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"应用信息"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜索"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"网络可能会\n受到监控"</string>
@@ -230,16 +227,20 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"触摸即可显示"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"勿扰"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"还有%d条"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+另外<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>条"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"不太紧急的通知会显示在下方"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"再次点按即可打开"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"向上滑动即可解锁"</string>
- <!-- no translation found for zen_mode_forever (7420011936770086993) -->
- <skip />
- <!-- no translation found for zen_mode_duration_minutes:one (9040808414992812341) -->
- <!-- no translation found for zen_mode_duration_minutes:other (6924190729213550991) -->
- <!-- no translation found for zen_mode_duration_hours:one (3480040795582254384) -->
- <!-- no translation found for zen_mode_duration_hours:other (5408537517529822157) -->
+ <string name="phone_hint" msgid="3101468054914424646">"向右滑动可打开拨号界面"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"向左滑动可打开相机"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"直到您将其关闭"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"正在充电(还需<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>才能充满)"</string>
+ <plurals name="zen_mode_duration_minutes">
+ <item quantity="one" msgid="9040808414992812341">"1分钟"</item>
+ <item quantity="other" msgid="6924190729213550991">"%d分钟"</item>
+ </plurals>
+ <plurals name="zen_mode_duration_hours">
+ <item quantity="one" msgid="3480040795582254384">"1小时"</item>
+ <item quantity="other" msgid="5408537517529822157">"%d小时"</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 04c00f5..25d9bce 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"開啟。"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"關閉。"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"已連線。"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"連線中。"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -160,7 +161,7 @@
<string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"鬧鐘已設定為:<xliff:g id="TIME">%s</xliff:g>。"</string>
<string name="accessibility_quick_settings_close" msgid="2571790856136835943">"關閉面板"</string>
<string name="accessibility_quick_settings_more_time" msgid="5778794273488176726">"更多時間"</string>
- <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"更少時間"</string>
+ <string name="accessibility_quick_settings_less_time" msgid="101026945195230084">"較少時間"</string>
<string name="data_usage_disabled_dialog_3g_title" msgid="5257833881698644687">"已停用 2G-3G 數據"</string>
<string name="data_usage_disabled_dialog_4g_title" msgid="4789143363492682629">"已停用 4G 數據"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="1046047248844821202">"已停用流動數據"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"藍牙 (<xliff:g id="NUMBER">%d</xliff:g> 部裝置)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"藍牙關閉"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"亮度"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"自動旋轉"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"已鎖定屏幕旋轉功能"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"已鎖定為直向"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"已鎖定為橫向"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自動旋轉"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"輪流展示鎖定"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"直向"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"橫向"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"輸入法"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"位置"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"位置關閉"</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未連線"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網絡"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 關閉"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"放送螢幕"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"放送螢幕"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"反轉顏色"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"網路共用"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"熱點"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"近期"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"沒有最近使用的應用程式"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資料"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"網絡可能會\n受到監控"</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"輕觸即可顯示"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"請勿騷擾"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"還有 %d 個"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"還有 <xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g> 則通知"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"不太緊急的通知會在下方顯示"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"再次輕按即可開啟"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"向上快速滑動即可解鎖"</string>
- <string name="zen_mode_forever" msgid="7420011936770086993">"手動關閉這項設定前一律啟用"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"向右快速滑動即可使用手機功能"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"向左快速滑動即可使用相機功能"</string>
+ <string name="zen_mode_forever" msgid="7420011936770086993">"直至您關閉這項設定"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後完成充電)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 分鐘"</item>
<item quantity="other" msgid="6924190729213550991">"%d 分鐘"</item>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 4ffb01b..4e4007a 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"開啟。"</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"關閉。"</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"已連線。"</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"連線中。"</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
@@ -187,10 +188,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"藍牙 (<xliff:g id="NUMBER">%d</xliff:g> 個裝置)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"關閉藍牙"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"亮度"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"自動旋轉"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"已鎖定螢幕旋轉功能"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"已鎖定為縱向"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"已鎖定為橫向"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"自動旋轉"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"已鎖定旋轉"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"縱向"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"橫向"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"輸入法"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"定位"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"定位服務已關閉"</string>
@@ -204,7 +205,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未連線"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網路"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 已關閉"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"投放螢幕"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"投放螢幕"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"反轉顏色"</string>
@@ -213,7 +214,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"網路共用"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"無線基地台"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"近期"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"沒有最近使用的應用程式"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"應用程式資訊"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"網路可能\n受到監控"</string>
@@ -226,13 +227,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"輕觸即可顯示"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"請勿打擾"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"還有 %d 則"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"還有 <xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g> 則通知"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"較不緊急的通知會顯示在下方"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"再次輕按即可開啟"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"向上滑動即可解鎖"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"向左滑動可使用手機功能"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"向左滑動可使用相機功能"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"手動關閉這項設定前一律啟用"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"充電中 (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>後充飽)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"1 分鐘"</item>
<item quantity="other" msgid="6924190729213550991">"%d 分鐘"</item>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 4c44972..7f292c1 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -120,6 +120,7 @@
<string name="accessibility_desc_on" msgid="2385254693624345265">"Vula."</string>
<string name="accessibility_desc_off" msgid="6475508157786853157">"Vala."</string>
<string name="accessibility_desc_connected" msgid="8366256693719499665">"Ixhunyiwe."</string>
+ <string name="accessibility_desc_connecting" msgid="3812924520316280149">"Iyaxhuma."</string>
<string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
<string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
<string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"I-HSPA"</string>
@@ -185,10 +186,10 @@
<string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"I-Bluetooth (<xliff:g id="NUMBER">%d</xliff:g> amadivayisi)"</string>
<string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"I-Bluetooth ivaliwe"</string>
<string name="quick_settings_brightness_label" msgid="6968372297018755815">"Ukugqama"</string>
- <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Ukuphendula ngokuzenzakalela"</string>
- <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Ukuphendula kukhiyiwe"</string>
- <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Kukhiyelwe ekumeni ngobude"</string>
- <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"Kukhiyelwe ekwakhiweni kwezwe"</string>
+ <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"Ukuphenduka okuzenzakalelayo"</string>
+ <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"Ukuphenduka kukhiyiwe"</string>
+ <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"Ukuma ngobude"</string>
+ <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"Ndlaleka okubanzi"</string>
<string name="quick_settings_ime_label" msgid="7073463064369468429">"Indlela yokungenayo"</string>
<string name="quick_settings_location_label" msgid="5011327048748762257">"Indawo"</string>
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Indawo ivaliwe"</string>
@@ -202,7 +203,7 @@
<string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Akuxhunyiwe"</string>
<string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ayikho inethiwekhi"</string>
<string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"I-Wi-Fi icimile"</string>
- <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Isikrini sabalingisi"</string>
+ <string name="quick_settings_remote_display_no_connection_label" msgid="7482103121002965053">"Isikrini sabadlali"</string>
<string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Ukugqama"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"OKUZENZAKALELAYO"</string>
<string name="quick_settings_inversion_label" msgid="8790919884718619648">"Faka imibala"</string>
@@ -211,7 +212,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"Ukusebenzisa njengemodemu"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"I-Hotspot"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"Izaziso"</string>
- <string name="recents_empty_message" msgid="2269156590813544104">"OKWAKAMUVA"</string>
+ <string name="recents_empty_message" msgid="7883614615463619450">"Azikho izinhlelo zokusebenza zakamuva"</string>
<string name="recents_app_info_button_label" msgid="2890317189376000030">"Ulwazi lohlelo lokusebenza"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"sesha"</string>
<string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Kungenzeka inethiwekhi\niqashiwe"</string>
@@ -224,13 +225,14 @@
</plurals>
<string name="zen_mode_notification_text" msgid="8336623711388065713">"Thinta ukuze ubonise"</string>
<string name="zen_mode_title" msgid="8793432092004749188">"Ungaphazamisi"</string>
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other" msgid="9180696159506883684">"%d okuningi"</item>
- </plurals>
+ <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
<string name="speed_bump_explanation" msgid="1288875699658819755">"Izaziso ezingasheshi kakhulu ezingezansi"</string>
<string name="notification_tap_again" msgid="7590196980943943842">"Thepha futhi ukuze uvule"</string>
<string name="keyguard_unlock" msgid="8043466894212841998">"Swayiphela phezulu ukuze uvule"</string>
+ <string name="phone_hint" msgid="3101468054914424646">"Swayiphela ngakwesokudla ukuze uthole ifoni"</string>
+ <string name="camera_hint" msgid="5241441720959174226">"Swayiphela ngakwesokunxele ukuze uthole ikhamela"</string>
<string name="zen_mode_forever" msgid="7420011936770086993">"Uze uvale lokhu"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Iyashaja (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ize igcwale)"</string>
<plurals name="zen_mode_duration_minutes">
<item quantity="one" msgid="9040808414992812341">"Iminithi elilodwa"</item>
<item quantity="other" msgid="6924190729213550991">"Amaminithi angu-%d"</item>
diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml
index 1ce4983..6628f3b 100644
--- a/packages/SystemUI/res/values/arrays.xml
+++ b/packages/SystemUI/res/values/arrays.xml
@@ -42,13 +42,11 @@
<!-- BatteryMeterView parameters -->
<array name="batterymeter_color_levels">
- <item>4</item>
<item>15</item>
<item>100</item>
</array>
<array name="batterymeter_color_values">
<item>#FFFF3300</item>
- <item>#FFFF3300</item>
<item>#FFFFFFFF</item>
</array>
<array name="batterymeter_bolt_points">
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 757d4ad..8c1a9c7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -29,7 +29,6 @@
<drawable name="status_bar_notification_row_background_color">#ff090909</drawable>
<color name="notification_list_shadow_top">#80000000</color>
<drawable name="recents_callout_line">#99ffffff</drawable>
- <drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable>
<drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
<color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white -->
<color name="batterymeter_charge_color">#FFFFFFFF</color>
@@ -42,23 +41,9 @@
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_tile_text">#FFFFFFFF</color>
<color name="status_bar_clock_color">#FFFFFFFF</color>
- <drawable name="notification_item_background_color">#ff111111</drawable>
- <drawable name="notification_item_background_color_pressed">#ff454545</drawable>
<!-- Tint color for the content on the notification overflow card. -->
- <color name="keyguard_overflow_content_color">#ff666666</color>
-
- <!-- The color of the red speed bump dot -->
- <color name="speed_bump_dot_red">#ffd50000</color>
-
- <!-- The color of the blue speed bump dot -->
- <color name="speed_bump_dot_blue">#ff2962ff</color>
-
- <!-- The color of the yellow speed bump dot -->
- <color name="speed_bump_dot_yellow">#ffffd600</color>
-
- <!-- The color of the green speed bump dot -->
- <color name="speed_bump_dot_green">#ff00c853</color>
+ <color name="keyguard_overflow_content_color">#ff686868</color>
<!-- The default recents task bar background color. -->
<color name="recents_task_bar_default_background_color">#e6444444</color>
@@ -77,7 +62,7 @@
<color name="keyguard_affordance">#ffffffff</color>
- <!-- Our quantum color palette (deep teal) -->
+ <!-- Our material color palette (deep teal) -->
<color name="primary_color">#ff7fcac3</color>
<color name="background_color_1">#ff384248</color>
<color name="background_color_1_press">#ff54656e</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0184df2..09a94f5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -108,22 +108,31 @@
<!-- milliseconds before the heads up notification accepts touches. -->
<integer name="heads_up_sensitivity_delay">700</integer>
+ <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
+ <integer name="recents_task_bar_dismiss_delay_seconds">3</integer>
<!-- The min animation duration for animating views that are currently visible. -->
<integer name="recents_filter_animate_current_views_min_duration">175</integer>
<!-- The min animation duration for animating views that are newly visible. -->
<integer name="recents_filter_animate_new_views_min_duration">125</integer>
<!-- The min animation duration for animating the task bar in. -->
- <integer name="recents_animate_task_bar_enter_duration">300</integer>
+ <integer name="recents_animate_task_bar_enter_duration">275</integer>
+ <!-- The animation delay for animating the first task in. This should roughly be the animation
+ duration of the transition in to recents. -->
+ <integer name="recents_animate_task_bar_enter_delay">225</integer>
<!-- The min animation duration for animating the task bar out. -->
- <integer name="recents_animate_task_bar_exit_duration">150</integer>
- <!-- The animation duration for animating in the info pane. -->
- <integer name="recents_animate_task_view_info_pane_duration">150</integer>
+ <integer name="recents_animate_task_bar_exit_duration">125</integer>
+ <!-- The min animation duration for animating the task in when transitioning from home. -->
+ <integer name="recents_animate_task_enter_from_home_duration">325</integer>
+ <!-- The animation stagger to apply to each task animation when transitioning from home. -->
+ <integer name="recents_animate_task_enter_from_home_delay">16</integer>
+ <!-- The min animation duration for animating the nav bar scrim in. -->
+ <integer name="recents_nav_bar_scrim_enter_duration">400</integer>
<!-- The animation duration for animating the removal of a task view. -->
<integer name="recents_animate_task_view_remove_duration">250</integer>
<!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
<integer name="recents_max_task_stack_view_dim">96</integer>
- <!-- Transposes the search bar layout in landscape -->
- <bool name="recents_transpose_search_layout_with_orientation">true</bool>
+ <!-- Transposes the recents layout in landscape. -->
+ <bool name="recents_transpose_layout_with_orientation">true</bool>
<!-- Whether to enable KeyguardService or not -->
<bool name="config_enableKeyguardService">true</bool>
@@ -138,5 +147,10 @@
<!-- Wait on the touch feedback this long before performing an action. -->
<integer name="feedback_start_delay">300</integer>
+
+ <!-- Set to true to enable the classic notification ticker that scrolls
+ Notification.tickerText across the status bar for what seems like an
+ eternity. -->
+ <bool name="enable_ticker">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bfbdcf3..5ffe3b3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -83,7 +83,7 @@
<dimen name="notification_mid_height">128dp</dimen>
<!-- Height of a the summary ("more card") notification on keyguard. -->
- <dimen name="notification_summary_height">40dp</dimen>
+ <dimen name="notification_summary_height">44dp</dimen>
<!-- size at which Notification icons will be drawn in the status bar -->
<dimen name="status_bar_icon_drawing_size">18dip</dimen>
@@ -235,9 +235,6 @@
<!-- The amount of highlight to make on each task view. -->
<dimen name="recents_task_view_highlight">1dp</dimen>
- <!-- The amount of space a user has to scroll to dismiss any info panes. -->
- <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
-
<!-- The height of the search bar space. -->
<dimen name="recents_search_bar_space_height">64dp</dimen>
@@ -272,14 +269,11 @@
<!-- The padding between the individual notification cards. -->
<dimen name="notification_padding">4dp</dimen>
- <!-- The height of the collapsed speed bump view. -->
- <dimen name="speed_bump_height_collapsed">24dp</dimen>
+ <!-- The minimum amount of top overscroll to go to the quick settings. -->
+ <dimen name="min_top_overscroll_to_qs">36dp</dimen>
- <!-- The padding inset the explanation text needs compared to the collapsed height -->
- <dimen name="speed_bump_text_padding_inset">10dp</dimen>
-
- <!-- The height of the speed bump dots. -->
- <dimen name="speed_bump_dots_height">5dp</dimen>
+ <!-- The height of the speed bump view. -->
+ <dimen name="speed_bump_height">16dp</dimen>
<!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) -->
<dimen name="collapsed_stack_height">94dp</dimen>
@@ -319,6 +313,23 @@
<!-- Volume panel z depth -->
<dimen name="volume_panel_z">3dp</dimen>
- <!-- Move distance for the hint animations on the lockscreen (unlock, phone, camera)-->
+ <!-- Distance between notifications and header when they are considered to be colliding. -->
+ <dimen name="header_notifications_collide_distance">24dp</dimen>
+
+ <!-- Distance the user needs to drag vertically such that a swipe is accepted to unlock the
+ device. -->
+ <dimen name="unlock_move_distance">75dp</dimen>
+
+ <!-- Move distance for the unlock hint animation on the lockscreen -->
<dimen name="hint_move_distance">75dp</dimen>
+
+ <!-- Move distance for the other hint animations on the lockscreen (phone, camera)-->
+ <dimen name="hint_move_distance_sideways">60dp</dimen>
+
+ <!-- The width of the region on the left/right edge of the screen for performing the camera/
+ phone hints. -->
+ <dimen name="edge_tap_area_width">48dp</dimen>
+
+ <!-- the distance the panel moves up when starting the up motion on Keyguard -->
+ <dimen name="keyguard_panel_move_up_distance">100dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/internal.xml b/packages/SystemUI/res/values/internal.xml
index ddaab942..7b93d31 100644
--- a/packages/SystemUI/res/values/internal.xml
+++ b/packages/SystemUI/res/values/internal.xml
@@ -17,6 +17,6 @@
<resources>
<dimen name="status_bar_height">@*android:dimen/status_bar_height</dimen>
<dimen name="navigation_bar_height">@*android:dimen/navigation_bar_height</dimen>
- <drawable name="notification_quantum_bg">@*android:drawable/notification_quantum_bg</drawable>
+ <drawable name="notification_material_bg">@*android:drawable/notification_material_bg</drawable>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 373f11f..e5499ee 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -59,25 +59,43 @@
<string name="status_bar_latest_events_title">Notifications</string>
<!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. [CHAR LIMIT=NONE]-->
- <string name="battery_low_title">Connect charger</string>
-
- <!-- When the battery is low, this is displayed to the user in a dialog. The subtitle of the low battery alert. [CHAR LIMIT=NONE] -->
- <string name="battery_low_subtitle">The battery is getting low.</string>
+ <string name="battery_low_title">Battery is low</string>
<!-- A message that appears when the battery level is getting low in a dialog. This is
appened to the subtitle of the low battery alert. "number" is the percentage of battery
remaining [CHAR LIMIT=none]-->
<string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g> remaining</string>
+ <!-- Same as battery_low_percent_format, with a notice about battery saver if on. [CHAR LIMIT=none]-->
+ <string name="battery_low_percent_format_saver_started"><xliff:g id="number">%d%%</xliff:g> remaining. Battery saver is on.</string>
+
<!-- A message that appears when a USB charger is plugged in and the device does not
support charging on it. That is, a charger that fits into the USB port and goes into
a wall socket, not into a computer. (This happens because some devices require more
current than the USB spec allows. [CHAR LIMIT=NONE] -->
<string name="invalid_charger">USB charging not supported.\nUse only the supplied charger.</string>
+ <!-- First line of invalid_charger, used in the notification form. [CHAR LIMIT=NONE]-->
+ <string name="invalid_charger_title">USB charging not supported.</string>
+
+ <!-- Second line of invalid_charger, used in the notification form. [CHAR LIMIT=NONE]-->
+ <string name="invalid_charger_text">Use only the supplied charger.</string>
+
<!-- When the battery is low, this is the label of the button to go to the
power usage activity to find out what drained the battery. [CHAR LIMIT=30] -->
- <string name="battery_low_why">Battery use</string>
+ <string name="battery_low_why">Settings</string>
+
+ <!-- Battery saver confirmation dialog title [CHAR LIMIT=NONE]-->
+ <string name="battery_saver_confirmation_title">Start battery saver?</string>
+
+ <!-- Battery saver confirmation dialog ok text [CHAR LIMIT=40]-->
+ <string name="battery_saver_confirmation_ok">Start</string>
+
+ <!-- Battery saver notification action [CHAR LIMIT=NONE]-->
+ <string name="battery_saver_start_action">Start battery saver</string>
+
+ <!-- Battery saver confirmation dialog text [CHAR LIMIT=NONE]-->
+ <string name="battery_saver_confirmation_text">To help improve battery life, Battery saver will reduce your device’s performance.\n\nBattery saver will be disabled when your device is plugged in.</string>
<!-- Name of the button that links to the Settings app. [CHAR LIMIT=NONE] -->
<string name="status_bar_settings_settings_button">Settings</string>
@@ -471,13 +489,13 @@
<!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
<string name="quick_settings_brightness_label">Brightness</string>
<!-- QuickSettings: Rotation Unlocked [CHAR LIMIT=NONE] -->
- <string name="quick_settings_rotation_unlocked_label">Auto Rotate</string>
+ <string name="quick_settings_rotation_unlocked_label">Auto-rotate</string>
<!-- QuickSettings: Rotation Locked [CHAR LIMIT=NONE] -->
- <string name="quick_settings_rotation_locked_label">Rotation Locked</string>
+ <string name="quick_settings_rotation_locked_label">Rotation locked</string>
<!-- QuickSettings: Locked to Portrait [CHAR LIMIT=NONE] -->
- <string name="quick_settings_rotation_locked_portrait_label">Locked to Portrait</string>
+ <string name="quick_settings_rotation_locked_portrait_label">Portrait</string>
<!-- QuickSettings: Locked to Landscape [CHAR LIMIT=NONE] -->
- <string name="quick_settings_rotation_locked_landscape_label">Locked to Landscape</string>
+ <string name="quick_settings_rotation_locked_landscape_label">Landscape</string>
<!-- QuickSettings: IME [CHAR LIMIT=NONE] -->
<string name="quick_settings_ime_label">Input Method</string>
<!-- QuickSettings: Location [CHAR LIMIT=NONE] -->
@@ -505,7 +523,7 @@
<!-- QuickSettings: Wifi (Off) [CHAR LIMIT=NONE] -->
<string name="quick_settings_wifi_off_label">Wi-Fi Off</string>
<!-- QuickSettings: Remote display [CHAR LIMIT=NONE] -->
- <string name="quick_settings_remote_display_no_connection_label">Cast Screen</string>
+ <string name="quick_settings_remote_display_no_connection_label">Cast screen</string>
<!-- QuickSettings: Brightness dialog title [CHAR LIMIT=NONE] -->
<string name="quick_settings_brightness_dialog_title">Brightness</string>
<!-- QuickSettings: Brightness dialog auto brightness button [CHAR LIMIT=NONE] -->
@@ -531,6 +549,18 @@
<string name="recents_search_bar_label">search</string>
+ <!-- Expanded Status Bar Header: Battery Charged [CHAR LIMIT=40] -->
+ <string name="expanded_header_battery_charged">Charged</string>
+
+ <!-- Expanded Status Bar Header: Charging, no known time [CHAR LIMIT=40] -->
+ <string name="expanded_header_battery_charging">Charging</string>
+
+ <!-- Expanded Status Bar Header: Charging, showing time left until charged [CHAR LIMIT=40] -->
+ <string name="expanded_header_battery_charging_with_time"><xliff:g id="charging_time" example="2 hrs 25 min">%s</xliff:g> until full</string>
+
+ <!-- Expanded Status Bar Header: Not charging [CHAR LIMIT=40] -->
+ <string name="expanded_header_battery_not_charging">Not charging</string>
+
<!-- Glyph to be overlaid atop the battery when the level is extremely low. Do not translate. -->
<string name="battery_meter_very_low_overlay_symbol">!</string>
@@ -558,10 +588,8 @@
<!-- Zen mode: Short title. [CHAR LIMIT=40] -->
<string name="zen_mode_title">Do not disturb</string>
- <!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=12] -->
- <plurals name="keyguard_more_overflow_text">
- <item quantity="other">%d more</item>
- </plurals>
+ <!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=1] -->
+ <string name="keyguard_more_overflow_text">+<xliff:g id="number_of_notifications" example="5">%d</xliff:g></string>
<!-- An explanation for the visual speed bump in the notifications, which will appear when you click on it. [CHAR LIMIT=50] -->
<string name="speed_bump_explanation">Less urgent notifications below</string>
@@ -572,11 +600,27 @@
<!-- Shows when people have pressed the unlock icon to explain how to unlock. [CHAR LIMIT=60] -->
<string name="keyguard_unlock">Swipe up to unlock</string>
+ <!-- Shows when people have clicked at the left edge of the screen to explain how to open the phone. In right-to-left languages, this is the opposite direction. [CHAR LIMIT=60] -->
+ <string name="phone_hint">Swipe right for phone</string>
+
+ <!-- Shows when people have clicked at the right edge of the screen to explain how to open the phone. In right-to-left languages, this is the opposite direction. [CHAR LIMIT=60] -->
+ <string name="camera_hint">Swipe left for camera</string>
+
<string name="bugreport_tile_extended" translatable="false">%s\n%s (%s)</string>
<!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
<string name="zen_mode_forever">Until you turn this off</string>
+ <!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
+ <string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
+ <!-- Related to user switcher --><skip/>
+ <!-- Name for the guest user -->
+ <string name="guest_nickname">Guest</string>
+
+ <!-- Label for adding a new guest -->
+ <string name="guest_new_guest">+ Guest</string>
+
<!-- Zen mode condition: time duration in minutes. [CHAR LIMIT=NONE] -->
<plurals name="zen_mode_duration_minutes">
<item quantity="one">For one minute</item>
@@ -588,4 +632,13 @@
<item quantity="one">For one hour</item>
<item quantity="other">For %d hours</item>
</plurals>
+
+ <!-- Battery saver notification title. [CHAR LIMIT=60]-->
+ <string name="battery_saver_notification_title">Battery saver is on</string>
+
+ <!-- Battery saver notification text. [CHAR LIMIT=60] -->
+ <string name="battery_saver_notification_text">Device performance is reduced.</string>
+
+ <!-- Battery saver notification action text. [CHAR LIMIT=60] -->
+ <string name="battery_saver_notification_action_text">Open battery saver settings</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6a12232..43560a3 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -70,17 +70,29 @@
<style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar" />
<style name="TextAppearance.StatusBar.Expanded.Clock">
- <item name="android:textSize">18dp</item>
+ <item name="android:textSize">16dp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">#ffffff</item>
</style>
<style name="TextAppearance.StatusBar.Expanded.Date">
+ <item name="android:textSize">14dp</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textColor">#99ffffff</item>
+ </style>
+
+ <style name="TextAppearance.StatusBar.Expanded.AboveDateTime">
<item name="android:textSize">12dp</item>
<item name="android:textStyle">normal</item>
- <item name="android:textColor">#afb3b6</item>
+ <item name="android:textColor">#99ffffff</item>
</style>
+ <style name="TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
+ parent="TextAppearance.StatusBar.Expanded.AboveDateTime" />
+
+ <style name="TextAppearance.StatusBar.Expanded.ChargingInfo"
+ parent="TextAppearance.StatusBar.Expanded.AboveDateTime" />
+
<style name="TextAppearance.StatusBar.Expanded.Network" parent="@style/TextAppearance.StatusBar.Expanded.Date">
<item name="android:textColor">#999999</item>
</style>
@@ -197,6 +209,7 @@
<style name="systemui_theme" parent="@android:style/Theme.DeviceDefault">
<item name="android:colorPrimary">@color/primary_color</item>
+ <item name="android:colorControlActivated">@color/system_accent_color</item>
</style>
<style name="NotificationsQuickSettings">
@@ -213,12 +226,7 @@
<item name="android:colorControlActivated">#ffffffff</item>
</style>
- <style name="QSAccentTheme" parent="@android:style/Theme.DeviceDefault">
- <item name="android:colorControlNormal">@color/system_accent_color</item>
- <item name="android:colorControlActivated">@color/system_accent_color</item>
- </style>
-
- <style name="BorderlessButton" parent="@android:style/Widget.Quantum.Button.Borderless" />
+ <style name="BorderlessButton" parent="@android:style/Widget.Material.Button.Borderless" />
<style name="BorderlessButton.Tiny">
<item name="android:minHeight">12dip</item>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 19d06be..5e48258 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -25,8 +25,6 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.BatteryManager;
@@ -44,7 +42,6 @@
private static final boolean SHOW_100_PERCENT = false;
private static final int FULL = 96;
- private static final int EMPTY = 4;
private static final float SUBPIXEL = 0.4f; // inset rects for softer edges
private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction
@@ -58,6 +55,7 @@
private int mHeight;
private int mWidth;
private String mWarningString;
+ private final int mCriticalLevel;
private final int mChargeColor;
private final float[] mBoltPoints;
private final Path mBoltPath = new Path();
@@ -197,6 +195,8 @@
mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(
context.getContentResolver(), "status_bar_show_battery_percent", 0);
mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
+ mCriticalLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFramePaint.setColor(frameColor);
@@ -303,7 +303,7 @@
if (level >= FULL) {
drawFrac = 1f;
- } else if (level <= EMPTY) {
+ } else if (level <= mCriticalLevel) {
drawFrac = 0f;
}
@@ -360,7 +360,7 @@
boolean pctOpaque = false;
float pctX = 0, pctY = 0;
String pctText = null;
- if (!tracker.plugged && level > EMPTY && mShowPercent
+ if (!tracker.plugged && level > mCriticalLevel && mShowPercent
&& !(tracker.level == 100 && !SHOW_100_PERCENT)) {
mTextPaint.setColor(getColorForLevel(level));
mTextPaint.setTextSize(height *
@@ -390,7 +390,7 @@
c.drawPath(mShapePath, mBatteryPaint);
if (!tracker.plugged) {
- if (level <= EMPTY) {
+ if (level <= mCriticalLevel) {
// draw the warning text
final float x = mWidth * 0.5f;
final float y = (mHeight + mWarningTextHeight) * 0.48f;
diff --git a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
index 4147155..14392b4 100644
--- a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
@@ -507,7 +507,6 @@
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 4d6d815c..006619b 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -42,6 +42,7 @@
boolean canChildBeExpanded(View v);
void setUserExpandedChild(View v, boolean userExpanded);
void setUserLockedChild(View v, boolean userLocked);
+ void expansionStateChanged(boolean isExpanding);
}
private static final String TAG = "ExpandHelper";
@@ -77,7 +78,6 @@
private boolean mWatchingForPull;
private boolean mHasPopped;
private View mEventSource;
- private View mCurrView;
private float mOldHeight;
private float mNaturalHeight;
private float mInitialTouchFocusY;
@@ -86,8 +86,7 @@
private float mLastFocusY;
private float mLastSpanY;
private int mTouchSlop;
- private int mLastMotionY;
- private float mPopLimit;
+ private float mLastMotionY;
private int mPopDuration;
private float mPullGestureMinXSpan;
private Callback mCallback;
@@ -95,10 +94,14 @@
private ViewScaler mScaler;
private ObjectAnimator mScaleAnimation;
private Vibrator mVibrator;
+ private boolean mEnabled = true;
+ private ExpandableView mResizedView;
+ private float mCurrentHeight;
private int mSmallSize;
private int mLargeSize;
private float mMaximumStretch;
+ private boolean mOnlyMovements;
private int mGravity;
@@ -109,17 +112,14 @@
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
if (DEBUG_SCALE) Log.v(TAG, "onscalebegin()");
- float focusX = detector.getFocusX();
- float focusY = detector.getFocusY();
- final ExpandableView underFocus = findView(focusX, focusY);
- startExpanding(underFocus, STRETCH);
+ startExpanding(mResizedView, STRETCH);
return mExpanding;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
- if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mCurrView);
+ if (DEBUG_SCALE) Log.v(TAG, "onscale() on " + mResizedView);
return true;
}
@@ -138,6 +138,7 @@
public void setHeight(float h) {
if (DEBUG_SCALE) Log.v(TAG, "SetHeight: setting to " + h);
mView.setActualHeight((int) h);
+ mCurrentHeight = h;
}
public float getHeight() {
return mView.getActualHeight();
@@ -165,7 +166,6 @@
mGravity = Gravity.TOP;
mScaleAnimation = ObjectAnimator.ofFloat(mScaler, "height", 0f);
mScaleAnimation.setDuration(EXPAND_DURATION);
- mPopLimit = mContext.getResources().getDimension(R.dimen.blinds_pop_threshold);
mPopDuration = mContext.getResources().getInteger(R.integer.blinds_pop_duration_ms);
mPullGestureMinXSpan = mContext.getResources().getDimension(R.dimen.pull_span_min);
@@ -188,7 +188,6 @@
float target = hand + mOldHeight;
float newHeight = clamp(target);
mScaler.setHeight(newHeight);
-
mLastFocusY = mSGD.getFocusY();
mLastSpanY = mSGD.getCurrentSpan();
}
@@ -252,6 +251,9 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (!isEnabled()) {
+ return false;
+ }
final int action = ev.getAction();
if (DEBUG_SCALE) Log.d(TAG, "intercept: act=" + MotionEvent.actionToString(action) +
" expanding=" + mExpanding +
@@ -270,38 +272,34 @@
if (DEBUG_SCALE) Log.d(TAG, "set initial span: " + mInitialTouchSpan);
if (mExpanding) {
+ mLastMotionY = ev.getRawY();
return true;
} else {
if ((action == MotionEvent.ACTION_MOVE) && 0 != (mExpansionStyle & BLINDS)) {
// we've begun Venetian blinds style expansion
return true;
}
- final float xspan = mSGD.getCurrentSpanX();
- if ((action == MotionEvent.ACTION_MOVE &&
- xspan > mPullGestureMinXSpan &&
- xspan > mSGD.getCurrentSpanY())) {
- // detect a vertical pulling gesture with fingers somewhat separated
- if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
-
- final ExpandableView underFocus = findView(x, y);
- startExpanding(underFocus, PULL);
- return true;
- }
- if (mScrollAdapter != null && !mScrollAdapter.isScrolledToTop()) {
- return false;
- }
- // Now look for other gestures
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
+ final float xspan = mSGD.getCurrentSpanX();
+ if (xspan > mPullGestureMinXSpan &&
+ xspan > mSGD.getCurrentSpanY() && !mExpanding) {
+ // detect a vertical pulling gesture with fingers somewhat separated
+ if (DEBUG_SCALE) Log.v(TAG, "got pull gesture (xspan=" + xspan + "px)");
+ startExpanding(mResizedView, PULL);
+ mWatchingForPull = false;
+ }
if (mWatchingForPull) {
- final int yDiff = y - mLastMotionY;
+ final float yDiff = ev.getRawY() - mInitialTouchY;
if (yDiff > mTouchSlop) {
if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
- mLastMotionY = y;
- final ExpandableView underFocus = findView(x, y);
- if (startExpanding(underFocus, BLINDS)) {
- mInitialTouchY = mLastMotionY;
- mHasPopped = false;
+ mWatchingForPull = false;
+ if (mResizedView != null && !isFullyExpanded(mResizedView)) {
+ if (startExpanding(mResizedView, BLINDS)) {
+ mLastMotionY = ev.getRawY();
+ mInitialTouchY = ev.getRawY();
+ mHasPopped = false;
+ }
}
}
}
@@ -310,8 +308,10 @@
case MotionEvent.ACTION_DOWN:
mWatchingForPull = mScrollAdapter != null &&
- isInside(mScrollAdapter.getHostView(), x, y);
- mLastMotionY = y;
+ isInside(mScrollAdapter.getHostView(), x, y)
+ && mScrollAdapter.isScrolledToTop();
+ mResizedView = findView(x, y);
+ mInitialTouchY = ev.getY();
break;
case MotionEvent.ACTION_CANCEL:
@@ -321,12 +321,28 @@
clearView();
break;
}
+ mLastMotionY = ev.getRawY();
return mExpanding;
}
}
+ public void setEnabled(boolean enable) {
+ mEnabled = enable;
+ }
+
+ private boolean isEnabled() {
+ return mEnabled;
+ }
+
+ private boolean isFullyExpanded(ExpandableView underFocus) {
+ return underFocus.getIntrinsicHeight() == underFocus.getMaxHeight();
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ if (!isEnabled()) {
+ return false;
+ }
final int action = ev.getActionMasked();
if (DEBUG_SCALE) Log.d(TAG, "touch: act=" + MotionEvent.actionToString(action) +
" expanding=" + mExpanding +
@@ -335,47 +351,71 @@
(0 != (mExpansionStyle & STRETCH) ? " (stretch)" : ""));
mSGD.onTouchEvent(ev);
+ final int x = (int) mSGD.getFocusX();
+ final int y = (int) mSGD.getFocusY();
+ if (mOnlyMovements) {
+ mLastMotionY = ev.getRawY();
+ return false;
+ }
switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mWatchingForPull = mScrollAdapter != null &&
+ isInside(mScrollAdapter.getHostView(), x, y);
+ mResizedView = findView(x, y);
+ mInitialTouchY = ev.getY();
+ break;
case MotionEvent.ACTION_MOVE: {
- if (0 != (mExpansionStyle & BLINDS)) {
- final float rawHeight = ev.getY() - mInitialTouchY + mOldHeight;
+ if (mWatchingForPull) {
+ final float yDiff = ev.getRawY() - mInitialTouchY;
+ if (yDiff > mTouchSlop) {
+ if (DEBUG) Log.v(TAG, "got venetian gesture (dy=" + yDiff + "px)");
+ mWatchingForPull = false;
+ if (mResizedView != null && !isFullyExpanded(mResizedView)) {
+ if (startExpanding(mResizedView, BLINDS)) {
+ mInitialTouchY = ev.getRawY();
+ mLastMotionY = ev.getRawY();
+ mHasPopped = false;
+ }
+ }
+ }
+ }
+ if (mExpanding && 0 != (mExpansionStyle & BLINDS)) {
+ final float rawHeight = ev.getRawY() - mLastMotionY + mCurrentHeight;
final float newHeight = clamp(rawHeight);
- final boolean wasClosed = (mOldHeight == mSmallSize);
boolean isFinished = false;
+ boolean expanded = false;
if (rawHeight > mNaturalHeight) {
isFinished = true;
+ expanded = true;
}
if (rawHeight < mSmallSize) {
isFinished = true;
+ expanded = false;
}
- final float pull = Math.abs(ev.getY() - mInitialTouchY);
- if (mHasPopped || pull > mPopLimit) {
- if (!mHasPopped) {
- vibrate(mPopDuration);
- mHasPopped = true;
- }
+ if (!mHasPopped) {
+ vibrate(mPopDuration);
+ mHasPopped = true;
}
- if (mHasPopped) {
- mScaler.setHeight(newHeight);
- }
-
- final int x = (int) mSGD.getFocusX();
- final int y = (int) mSGD.getFocusY();
- ExpandableView underFocus = findView(x, y);
- if (isFinished && underFocus != null && underFocus != mCurrView) {
- finishExpanding(false); // @@@ needed?
- startExpanding(underFocus, BLINDS);
- mInitialTouchY = y;
- mHasPopped = false;
+ mScaler.setHeight(newHeight);
+ mLastMotionY = ev.getRawY();
+ if (isFinished) {
+ mCallback.setUserExpandedChild(mResizedView, expanded);
+ mCallback.expansionStateChanged(false);
+ return false;
+ } else {
+ mCallback.expansionStateChanged(true);
}
return true;
}
if (mExpanding) {
+
+ // Gestural expansion is running
updateExpansion();
+ mLastMotionY = ev.getRawY();
return true;
}
@@ -396,7 +436,8 @@
clearView();
break;
}
- return true;
+ mLastMotionY = ev.getRawY();
+ return mResizedView != null;
}
/**
@@ -407,15 +448,16 @@
return false;
}
mExpansionStyle = expandType;
- if (mExpanding && v == mCurrView) {
+ if (mExpanding && v == mResizedView) {
return true;
}
mExpanding = true;
+ mCallback.expansionStateChanged(true);
if (DEBUG) Log.d(TAG, "scale type " + expandType + " beginning on view: " + v);
mCallback.setUserLockedChild(v, true);
- setView(v);
- mScaler.setView((ExpandableView) v);
+ mScaler.setView(v);
mOldHeight = mScaler.getHeight();
+ mCurrentHeight = mOldHeight;
if (mCallback.canChildBeExpanded(v)) {
if (DEBUG) Log.d(TAG, "working on an expandable child");
mNaturalHeight = mScaler.getNaturalHeight(mLargeSize);
@@ -425,14 +467,13 @@
}
if (DEBUG) Log.d(TAG, "got mOldHeight: " + mOldHeight +
" mNaturalHeight: " + mNaturalHeight);
- v.getParent().requestDisallowInterceptTouchEvent(true);
return true;
}
private void finishExpanding(boolean force) {
if (!mExpanding) return;
- if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mCurrView);
+ if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mResizedView);
float currentHeight = mScaler.getHeight();
float targetHeight = mSmallSize;
@@ -446,11 +487,12 @@
if (mScaleAnimation.isRunning()) {
mScaleAnimation.cancel();
}
- mCallback.setUserExpandedChild(mCurrView, targetHeight == mNaturalHeight);
+ mCallback.setUserExpandedChild(mResizedView, targetHeight == mNaturalHeight);
+ mCallback.expansionStateChanged(false);
if (targetHeight != currentHeight) {
mScaleAnimation.setFloatValues(targetHeight);
mScaleAnimation.setupStartValues();
- final View scaledView = mCurrView;
+ final View scaledView = mResizedView;
mScaleAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -460,7 +502,7 @@
});
mScaleAnimation.start();
} else {
- mCallback.setUserLockedChild(mCurrView, false);
+ mCallback.setUserLockedChild(mResizedView, false);
}
mExpanding = false;
@@ -470,16 +512,11 @@
if (DEBUG) Log.d(TAG, "currentHeight is: " + currentHeight);
if (DEBUG) Log.d(TAG, "mSmallSize is: " + mSmallSize);
if (DEBUG) Log.d(TAG, "targetHeight is: " + targetHeight);
- if (DEBUG) Log.d(TAG, "scale was finished on view: " + mCurrView);
+ if (DEBUG) Log.d(TAG, "scale was finished on view: " + mResizedView);
}
private void clearView() {
- mCurrView = null;
-
- }
-
- private void setView(View v) {
- mCurrView = v;
+ mResizedView = null;
}
/**
@@ -494,6 +531,18 @@
}
/**
+ * Change the expansion mode to only observe movements and don't perform any resizing.
+ * This is needed when the expanding is finished and the scroller kicks in,
+ * performing an overscroll motion. We only want to shrink it again when we are not
+ * overscrolled.
+ *
+ * @param onlyMovements Should only movements be observed?
+ */
+ public void onlyObserveMovements(boolean onlyMovements) {
+ mOnlyMovements = onlyMovements;
+ }
+
+ /**
* Triggers haptic feedback.
*/
private synchronized void vibrate(long duration) {
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 7c85712..9650435 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -20,9 +20,14 @@
import android.view.View;
public interface RecentsComponent {
+ public interface Callbacks {
+ public void onVisibilityChanged(boolean visible);
+ }
+
void showRecents(boolean triggeredFromAltTab, View statusBarView);
void hideRecents(boolean triggeredFromAltTab);
void toggleRecents(Display display, int layoutDirection, View statusBarView);
void preloadRecents();
void cancelPreloadingRecents();
+ void setCallback(Callbacks cb);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4837a53..ffd76a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -878,6 +878,7 @@
if (mLockPatternUtils.checkVoldPassword()) {
if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
// Without this, settings is not enabled until the lock screen first appears
+ mShowing = false;
hideLocked();
return;
}
@@ -1191,9 +1192,17 @@
if (DEBUG) Log.d(TAG, "handleHide");
try {
- // Don't actually hide the Keyguard at the moment, wait for window manager until
- // it tells us it's safe to do so with startKeyguardExitAnimation.
- mWM.keyguardGoingAway();
+ if (mShowing) {
+
+ // Don't actually hide the Keyguard at the moment, wait for window manager until
+ // it tells us it's safe to do so with startKeyguardExitAnimation.
+ mWM.keyguardGoingAway();
+ } else {
+
+ // Don't try to rely on WindowManager - if Keyguard wasn't showing, window
+ // manager won't start the exit animation.
+ handleStartKeyguardExitAnimation(0, 0);
+ }
} catch (RemoteException e) {
Log.e(TAG, "Error while calling WindowManager", e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java
index feec87c..2943494 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java
@@ -16,6 +16,7 @@
package com.android.systemui.power;
+import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
@@ -25,13 +26,13 @@
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
-import android.view.View;
+import android.view.ContextThemeWrapper;
import android.view.WindowManager;
-import android.widget.TextView;
import com.android.systemui.R;
@@ -46,13 +47,14 @@
private int mBatteryLevel;
private int mBucket;
private long mScreenOffTime;
+ private boolean mSaver;
+ private int mSaverTriggerLevel;
private AlertDialog mInvalidChargerDialog;
private AlertDialog mLowBatteryDialog;
- private TextView mBatteryLevelTextView;
public PowerDialogWarnings(Context context) {
- mContext = context;
+ mContext = new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_Light);
}
@Override
@@ -77,7 +79,7 @@
@Override
public void updateLowBatteryWarning() {
- if (mBatteryLevelTextView != null) {
+ if (mLowBatteryDialog != null) {
showLowBatteryWarning(false /*playSound*/);
}
}
@@ -93,27 +95,22 @@
@Override
public void showLowBatteryWarning(boolean playSound) {
Slog.i(TAG,
- ((mBatteryLevelTextView == null) ? "showing" : "updating")
+ ((mLowBatteryDialog == null) ? "showing" : "updating")
+ " low battery warning: level=" + mBatteryLevel
+ " [" + mBucket + "]");
- CharSequence levelText = mContext.getString(
- R.string.battery_low_percent_format, mBatteryLevel);
+ final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started
+ : R.string.battery_low_percent_format;
+ final CharSequence levelText = mContext.getString(textRes, mBatteryLevel);
- if (mBatteryLevelTextView != null) {
- mBatteryLevelTextView.setText(levelText);
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.setMessage(levelText);
} else {
- View v = View.inflate(mContext, R.layout.battery_low, null);
- mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);
-
- mBatteryLevelTextView.setText(levelText);
-
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
- b.setCancelable(true);
- b.setTitle(R.string.battery_low_title);
- b.setView(v);
- b.setIconAttribute(android.R.attr.alertDialogIcon);
- b.setPositiveButton(android.R.string.ok, null);
+ b.setCancelable(true);
+ b.setTitle(R.string.battery_low_title);
+ b.setMessage(levelText);
+ b.setPositiveButton(android.R.string.ok, null);
final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -125,6 +122,11 @@
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
+ try {
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ // we tried
+ }
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
dismissLowBatteryWarning();
}
@@ -136,10 +138,9 @@
@Override
public void onDismiss(DialogInterface dialog) {
mLowBatteryDialog = null;
- mBatteryLevelTextView = null;
}
});
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
d.getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
d.show();
@@ -198,21 +199,32 @@
dismissLowBatteryWarning();
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
- b.setCancelable(true);
- b.setMessage(R.string.invalid_charger);
- b.setIconAttribute(android.R.attr.alertDialogIcon);
- b.setPositiveButton(android.R.string.ok, null);
+ b.setCancelable(true);
+ b.setTitle(R.string.invalid_charger_title);
+ b.setMessage(R.string.invalid_charger_text);
+ b.setPositiveButton(android.R.string.ok, null);
AlertDialog d = b.create();
d.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
mInvalidChargerDialog = null;
- mBatteryLevelTextView = null;
}
});
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.getWindow().getAttributes().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
d.show();
mInvalidChargerDialog = d;
}
+
+ @Override
+ public void showSaverMode(boolean mode) {
+ mSaver = mode;
+ }
+
+ @Override
+ public void setSaverTrigger(int level) {
+ mSaverTriggerLevel = level;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
new file mode 100644
index 0000000..e8f3745
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.power;
+
+import android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+import java.io.PrintWriter;
+
+public class PowerNotificationWarnings implements PowerUI.WarningsUI {
+ private static final String TAG = PowerUI.TAG + ".Notification";
+ private static final boolean DEBUG = PowerUI.DEBUG;
+
+ private static final String TAG_NOTIFICATION = "low_battery";
+ private static final int ID_NOTIFICATION = 100;
+ private static final int AUTO_DISMISS_MS = 10000;
+
+ private static final int SHOWING_NOTHING = 0;
+ private static final int SHOWING_WARNING = 1;
+ private static final int SHOWING_SAVER = 2;
+ private static final int SHOWING_INVALID_CHARGER = 3;
+ private static final String[] SHOWING_STRINGS = {
+ "SHOWING_NOTHING",
+ "SHOWING_WARNING",
+ "SHOWING_SAVER",
+ "SHOWING_INVALID_CHARGER",
+ };
+
+ private static final String ACTION_SHOW_FALLBACK_WARNING = "PNW.warningFallback";
+ private static final String ACTION_SHOW_FALLBACK_CHARGER = "PNW.chargerFallback";
+ private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
+ private static final String ACTION_START_SAVER = "PNW.startSaver";
+
+ private final Context mContext;
+ private final Context mLightContext;
+ private final NotificationManager mNoMan;
+ private final Handler mHandler = new Handler();
+ private final PowerDialogWarnings mFallbackDialogs;
+ private final Receiver mReceiver = new Receiver();
+ private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
+ private final Intent mOpenSaverSettings = settings(Settings.ACTION_BATTERY_SAVER_SETTINGS);
+
+ private int mBatteryLevel;
+ private int mBucket;
+ private long mScreenOffTime;
+ private int mShowing;
+
+ private boolean mSaver;
+ private int mSaverTriggerLevel;
+ private boolean mWarning;
+ private boolean mPlaySound;
+ private boolean mInvalidCharger;
+
+ public PowerNotificationWarnings(Context context) {
+ mContext = context;
+ mLightContext = new ContextThemeWrapper(mContext,
+ android.R.style.Theme_DeviceDefault_Light);
+ mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ mFallbackDialogs = new PowerDialogWarnings(context);
+ mReceiver.init();
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print("mSaver="); pw.println(mSaver);
+ pw.print("mWarning="); pw.println(mWarning);
+ pw.print("mPlaySound="); pw.println(mPlaySound);
+ pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
+ pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]);
+ }
+
+ @Override
+ public void update(int batteryLevel, int bucket, long screenOffTime) {
+ mBatteryLevel = batteryLevel;
+ mBucket = bucket;
+ mScreenOffTime = screenOffTime;
+ mFallbackDialogs.update(batteryLevel, bucket, screenOffTime);
+ }
+
+ @Override
+ public void showSaverMode(boolean mode) {
+ mSaver = mode;
+ updateNotification();
+ }
+
+ @Override
+ public void setSaverTrigger(int level) {
+ mSaverTriggerLevel = level;
+ updateNotification();
+ }
+
+ private void updateNotification() {
+ Slog.d(TAG, "updateNotification mWarning=" + mWarning
+ + " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger);
+ if (mInvalidCharger) {
+ showInvalidChargerNotification();
+ mShowing = SHOWING_INVALID_CHARGER;
+ } else if (mWarning) {
+ showWarningNotification();
+ mShowing = SHOWING_WARNING;
+ } else if (mSaver) {
+ showSaverNotification();
+ mShowing = SHOWING_SAVER;
+ } else {
+ mNoMan.cancel(TAG_NOTIFICATION, ID_NOTIFICATION);
+ mShowing = SHOWING_NOTHING;
+ }
+ }
+
+ private void showInvalidChargerNotification() {
+ final Notification.Builder nb = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_power_low)
+ .setShowWhen(false)
+ .setOngoing(true)
+ .setContentTitle(mContext.getString(R.string.invalid_charger_title))
+ .setContentText(mContext.getString(R.string.invalid_charger_text))
+ .setPriority(Notification.PRIORITY_MAX)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_CHARGER), true);
+ final Notification n = nb.build();
+ if (n.headsUpContentView != null) {
+ n.headsUpContentView.setViewVisibility(com.android.internal.R.id.right_icon, View.GONE);
+ }
+ mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, n, UserHandle.CURRENT);
+ }
+
+ private void showWarningNotification() {
+ final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started
+ : R.string.battery_low_percent_format;
+ final Notification.Builder nb = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_power_low)
+ .setShowWhen(false)
+ .setContentTitle(mContext.getString(R.string.battery_low_title))
+ .setContentText(mContext.getString(textRes, mBatteryLevel))
+ .setOngoing(true)
+ .setPriority(Notification.PRIORITY_MAX)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_WARNING), true);
+ if (hasBatterySettings()) {
+ nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
+ }
+ if (!mSaver && mSaverTriggerLevel <= 0) {
+ nb.addAction(R.drawable.ic_power_saver,
+ mContext.getString(R.string.battery_saver_start_action),
+ pendingBroadcast(ACTION_START_SAVER));
+ }
+ if (mPlaySound) {
+ attachLowBatterySound(nb);
+ }
+ final Notification n = nb.build();
+ if (n.headsUpContentView != null) {
+ n.headsUpContentView.setViewVisibility(com.android.internal.R.id.right_icon, View.GONE);
+ }
+ mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, n, UserHandle.CURRENT);
+ }
+
+ private void showSaverNotification() {
+ final Notification.Builder nb = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_power_saver)
+ .setContentTitle(mContext.getString(R.string.battery_saver_notification_title))
+ .setContentText(mContext.getString(R.string.battery_saver_notification_text))
+ .setOngoing(true)
+ .setShowWhen(false)
+ .setCategory(Notification.CATEGORY_SYSTEM);
+ if (hasSaverSettings()) {
+ nb.addAction(0,
+ mContext.getString(R.string.battery_saver_notification_action_text),
+ pendingActivity(mOpenSaverSettings));
+ nb.setContentIntent(pendingActivity(mOpenSaverSettings));
+ }
+ mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, nb.build(), UserHandle.CURRENT);
+ }
+
+ private PendingIntent pendingActivity(Intent intent) {
+ return PendingIntent.getActivityAsUser(mContext,
+ 0, intent, 0, null, UserHandle.CURRENT);
+ }
+
+ private PendingIntent pendingBroadcast(String action) {
+ return PendingIntent.getBroadcastAsUser(mContext,
+ 0, new Intent(action), 0, UserHandle.CURRENT);
+ }
+
+ private static Intent settings(String action) {
+ return new Intent(action).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ }
+
+ @Override
+ public boolean isInvalidChargerWarningShowing() {
+ return mInvalidCharger;
+ }
+
+ @Override
+ public void updateLowBatteryWarning() {
+ updateNotification();
+ mFallbackDialogs.updateLowBatteryWarning();
+ }
+
+ @Override
+ public void dismissLowBatteryWarning() {
+ Slog.i(TAG, "dismissing low battery warning: level=" + mBatteryLevel);
+ dismissLowBatteryNotification();
+ mFallbackDialogs.dismissLowBatteryWarning();
+ }
+
+ private void dismissLowBatteryNotification() {
+ Slog.i(TAG, "dismissing low battery notification");
+ mWarning = false;
+ updateNotification();
+ }
+
+ private boolean hasBatterySettings() {
+ return mOpenBatterySettings.resolveActivity(mContext.getPackageManager()) != null;
+ }
+
+ private boolean hasSaverSettings() {
+ return mOpenSaverSettings.resolveActivity(mContext.getPackageManager()) != null;
+ }
+
+ @Override
+ public void showLowBatteryWarning(boolean playSound) {
+ Slog.i(TAG,
+ "show low battery warning: level=" + mBatteryLevel
+ + " [" + mBucket + "]");
+ mPlaySound = playSound;
+ mWarning = true;
+ updateNotification();
+ mHandler.removeCallbacks(mDismissLowBatteryNotification);
+ mHandler.postDelayed(mDismissLowBatteryNotification, AUTO_DISMISS_MS);
+ }
+
+ private void attachLowBatterySound(Notification.Builder b) {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ final int silenceAfter = Settings.Global.getInt(cr,
+ Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);
+ final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;
+ if (silenceAfter > 0
+ && mScreenOffTime > 0
+ && offTime > silenceAfter) {
+ Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter
+ + "ms): not waking up the user with low battery sound");
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated
+ }
+
+ if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {
+ final String soundPath = Settings.Global.getString(cr,
+ Settings.Global.LOW_BATTERY_SOUND);
+ if (soundPath != null) {
+ final Uri soundUri = Uri.parse("file://" + soundPath);
+ if (soundUri != null) {
+ b.setSound(soundUri, AudioManager.STREAM_SYSTEM);
+ Slog.d(TAG, "playing sound " + soundUri);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void dismissInvalidChargerWarning() {
+ dismissInvalidChargerNotification();
+ mFallbackDialogs.dismissInvalidChargerWarning();
+ }
+
+ private void dismissInvalidChargerNotification() {
+ Slog.i(TAG, "dismissing invalid charger notification");
+ mInvalidCharger = false;
+ updateNotification();
+ }
+
+ @Override
+ public void showInvalidChargerWarning() {
+ mInvalidCharger = true;
+ updateNotification();
+ }
+
+ private void showStartSaverConfirmation() {
+ final AlertDialog d = new AlertDialog.Builder(mLightContext)
+ .setTitle(R.string.battery_saver_confirmation_title)
+ .setMessage(R.string.battery_saver_confirmation_text)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(R.string.battery_saver_confirmation_ok, mStartSaverMode)
+ .create();
+
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.getWindow().getAttributes().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ d.show();
+ }
+
+ private void setSaverSetting(boolean mode) {
+ final int val = mode ? 1 : 0;
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, val);
+ }
+
+ private final class Receiver extends BroadcastReceiver {
+
+ public void init() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SHOW_FALLBACK_WARNING);
+ filter.addAction(ACTION_SHOW_FALLBACK_CHARGER);
+ filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
+ filter.addAction(ACTION_START_SAVER);
+ mContext.registerReceiver(this, filter, null, mHandler);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ Slog.i(TAG, "Received " + action);
+ if (action.equals(ACTION_SHOW_FALLBACK_WARNING)) {
+ dismissLowBatteryNotification();
+ mFallbackDialogs.showLowBatteryWarning(false /*playSound*/);
+ } else if (action.equals(ACTION_SHOW_FALLBACK_CHARGER)) {
+ dismissInvalidChargerNotification();
+ mFallbackDialogs.showInvalidChargerWarning();
+ } else if (action.equals(ACTION_SHOW_BATTERY_SETTINGS)) {
+ dismissLowBatteryNotification();
+ mContext.startActivityAsUser(mOpenBatterySettings, UserHandle.CURRENT);
+ } else if (action.equals(ACTION_START_SAVER)) {
+ dismissLowBatteryNotification();
+ showStartSaverConfirmation();
+ }
+ }
+ }
+
+ private final OnClickListener mStartSaverMode = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ setSaverSetting(true);
+ }
+ });
+ }
+ };
+
+ private final Runnable mDismissLowBatteryNotification = new Runnable() {
+ @Override
+ public void run() {
+ dismissLowBatteryNotification();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 0fb0f8b..1bb7edb 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -17,13 +17,17 @@
package com.android.systemui.power;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
@@ -35,13 +39,15 @@
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
-
static final boolean DEBUG = false;
- private WarningsUI mWarnings;
private final Handler mHandler = new Handler();
+ private final SettingsObserver mObserver = new SettingsObserver(mHandler);
+ private final Receiver mReceiver = new Receiver();
+ private PowerManager mPowerManager;
+ private WarningsUI mWarnings;
private int mBatteryLevel = 100;
private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
private int mPlugType = 0;
@@ -53,24 +59,54 @@
private long mScreenOffTime = -1;
public void start() {
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
+ mWarnings = new PowerNotificationWarnings(mContext);
- mLowBatteryAlertCloseLevel = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
- mLowBatteryReminderLevels[0] = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_lowBatteryWarningLevel);
- mLowBatteryReminderLevels[1] = mContext.getResources().getInteger(
+ ContentObserver obs = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateBatteryWarningLevels();
+ }
+ };
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+ false, obs, UserHandle.USER_ALL);
+ updateBatteryWarningLevels();
+ mReceiver.init();
+ mObserver.init();
+ }
+
+ private void setSaverMode(boolean mode) {
+ mWarnings.showSaverMode(mode);
+ }
+
+ private void setSaverTrigger(int level) {
+ mWarnings.setSaverTrigger(level);
+ }
+
+ void updateBatteryWarningLevels() {
+ int critLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
- final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
- mWarnings = new PowerDialogWarnings(mContext);
+ final ContentResolver resolver = mContext.getContentResolver();
+ int defWarnLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryWarningLevel);
+ int warnLevel = Settings.Global.getInt(resolver,
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
+ if (warnLevel == 0) {
+ warnLevel = defWarnLevel;
+ }
+ if (warnLevel < critLevel) {
+ warnLevel = critLevel;
+ }
- // Register for Intent broadcasts for...
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_SCREEN_ON);
- mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+ mLowBatteryReminderLevels[0] = warnLevel;
+ mLowBatteryReminderLevels[1] = critLevel;
+ mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
+ + mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
}
/**
@@ -87,7 +123,7 @@
if (level >= mLowBatteryAlertCloseLevel) {
return 1;
}
- if (level >= mLowBatteryReminderLevels[0]) {
+ if (level > mLowBatteryReminderLevels[0]) {
return 0;
}
final int N = mLowBatteryReminderLevels.length;
@@ -99,7 +135,23 @@
throw new RuntimeException("not possible!");
}
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ private final class Receiver extends BroadcastReceiver {
+
+ public void init() {
+ // Register for Intent broadcasts for...
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ mContext.registerReceiver(this, filter, null, mHandler);
+ updateSaverMode();
+ }
+
+ private void updateSaverMode() {
+ setSaverMode(mPowerManager.isPowerSaveMode());
+ }
+
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -160,6 +212,8 @@
mScreenOffTime = SystemClock.elapsedRealtime();
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOffTime = -1;
+ } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
+ updateSaverMode();
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
@@ -197,6 +251,8 @@
public interface WarningsUI {
void update(int batteryLevel, int bucket, long screenOffTime);
+ void setSaverTrigger(int level);
+ void showSaverMode(boolean mode);
void dismissLowBatteryWarning();
void showLowBatteryWarning(boolean playSound);
void dismissInvalidChargerWarning();
@@ -205,5 +261,29 @@
boolean isInvalidChargerWarningShowing();
void dump(PrintWriter pw);
}
+
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri LOW_POWER_MODE_TRIGGER_LEVEL_URI =
+ Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL);
+
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void init() {
+ onChange(true, LOW_POWER_MODE_TRIGGER_LEVEL_URI);
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(LOW_POWER_MODE_TRIGGER_LEVEL_URI, false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (LOW_POWER_MODE_TRIGGER_LEVEL_URI.equals(uri)) {
+ final int level = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ setSaverTrigger(level);
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 2bf369a..07ffd66 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -156,12 +156,14 @@
AnimatorListener listener = null;
if (show) {
if (mDetailRecord != null) return;
- final View detail = r.tile.createDetailView(mContext, mDetail);
- if (detail == null) return;
+ if (r.detailView == null) {
+ r.detailView = r.tile.createDetailView(mContext, mDetail);
+ }
+ if (r.detailView == null) return;
mDetailRecord = r;
mDetail.removeAllViews();
mDetail.bringToFront();
- mDetail.addView(detail);
+ mDetail.addView(r.detailView);
} else {
if (mDetailRecord == null) return;
listener = mTeardownDetailWhenDone;
@@ -273,6 +275,7 @@
private static final class TileRecord {
QSTile<?> tile;
QSTileView tileView;
+ View detailView;
int row;
int col;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index c76ee8c..786cd9e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -287,6 +287,7 @@
public boolean activityIn;
public boolean activityOut;
public int overlayIconId;
+ public boolean filter;
@Override
public boolean copyTo(State other) {
@@ -300,6 +301,7 @@
o.activityIn = activityIn;
o.activityOut = activityOut;
o.overlayIconId = overlayIconId;
+ o.filter = filter;
return super.copyTo(other) || changed;
}
@@ -311,6 +313,7 @@
rt.insert(rt.length() - 1, ",activityIn=" + activityIn);
rt.insert(rt.length() - 1, ",activityOut=" + activityOut);
rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId);
+ rt.insert(rt.length() - 1, ",filter=" + filter);
return rt;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 2edd8d5..5388994 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -135,7 +135,7 @@
}
private Drawable getTileBackground() {
- final int[] attrs = new int[] { android.R.attr.selectableItemBackground};
+ final int[] attrs = new int[] { android.R.attr.selectableItemBackgroundBorderless };
final TypedArray ta = mContext.obtainStyledAttributes(attrs);
final Drawable d = ta.getDrawable(0);
ta.recycle();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index 7b6c544..d5fe033 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -18,6 +18,8 @@
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.ColorFilter;
+import android.graphics.LightingColorFilter;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -29,6 +31,7 @@
public final class SignalTileView extends QSTileView {
private static final long DEFAULT_DURATION = new ValueAnimator().getDuration();
private static final long SHORT_DURATION = DEFAULT_DURATION / 3;
+ private static final ColorFilter FILTER = new LightingColorFilter(0xffffffff, 0xff283034);
private FrameLayout mIconFrame;
private ImageView mSignal;
@@ -41,10 +44,12 @@
mIn = new ImageView(context);
mIn.setImageResource(R.drawable.ic_qs_signal_in);
+ mIn.setColorFilter(FILTER);
addView(mIn);
mOut = new ImageView(context);
mOut.setImageResource(R.drawable.ic_qs_signal_out);
+ mOut.setColorFilter(FILTER);
addView(mOut);
}
@@ -88,10 +93,12 @@
final SignalState s = (SignalState) state;
mSignal.setImageDrawable(null); // force refresh
mSignal.setImageResource(s.iconId);
+ mSignal.setColorFilter(s.filter ? FILTER : null);
if (s.overlayIconId > 0) {
mOverlay.setVisibility(VISIBLE);
mOverlay.setImageDrawable(null); // force refresh
mOverlay.setImageResource(s.overlayIconId);
+ mOverlay.setColorFilter(s.filter ? FILTER : null);
} else {
mOverlay.setVisibility(GONE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index d220e1a..7431e69 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -84,7 +84,7 @@
} else if (connecting) {
state.iconId = R.drawable.ic_qs_bluetooth_connecting;
stateContentDescription = mContext.getString(R.string.accessibility_desc_connecting);
- state.label = mController.getLastDeviceName();
+ state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
} else {
state.iconId = R.drawable.ic_qs_bluetooth_on;
stateContentDescription = mContext.getString(R.string.accessibility_desc_on);
@@ -94,7 +94,7 @@
}
} else {
state.iconId = R.drawable.ic_qs_bluetooth_off;
- state.label = mContext.getString(R.string.quick_settings_bluetooth_off_label);
+ state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
stateContentDescription = mContext.getString(R.string.accessibility_desc_off);
}
state.contentDescription = mContext.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 182a0ce..6d91d33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -72,12 +72,15 @@
if (cb == null) return;
final Resources r = mContext.getResources();
- state.iconId = cb.enabled && (cb.mobileSignalIconId > 0)
+ state.iconId = cb.noSim
+ ? R.drawable.stat_sys_no_sim
+ : cb.enabled && (cb.mobileSignalIconId > 0)
? cb.mobileSignalIconId
: R.drawable.ic_qs_signal_no_signal;
state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled
? cb.dataTypeIconId
: 0;
+ state.filter = state.iconId != R.drawable.stat_sys_no_sim;
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
@@ -117,6 +120,7 @@
boolean activityIn;
boolean activityOut;
String enabledDesc;
+ boolean noSim;
}
private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() {
@@ -134,7 +138,7 @@
int mobileSignalIconId,
String mobileSignalContentDescriptionId, int dataTypeIconId,
boolean activityIn, boolean activityOut,
- String dataTypeContentDescriptionId, String description) {
+ String dataTypeContentDescriptionId, String description, boolean noSim) {
final CallbackInfo info = new CallbackInfo(); // TODO pool?
info.enabled = enabled;
info.wifiEnabled = mWifiEnabled;
@@ -145,6 +149,7 @@
info.activityIn = activityIn;
info.activityOut = activityOut;
info.enabledDesc = description;
+ info.noSim = noSim;
refreshState(info);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 5301362..7c2c7c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -71,6 +71,6 @@
state.visible = mVisible;
state.value = enabled;
state.label = mContext.getString(R.string.quick_settings_inversion_label);
- state.iconId = R.drawable.ic_qs_color_inversion;
+ state.iconId = enabled ? R.drawable.ic_qs_inversion_on : R.drawable.ic_qs_inversion_off;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index e496468..04f1eb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -16,9 +16,6 @@
package com.android.systemui.qs.tiles;
-import android.content.res.Resources;
-import android.graphics.drawable.AnimationDrawable;
-
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.LocationController;
@@ -63,29 +60,16 @@
protected void handleUpdateState(BooleanState state, Object arg) {
final boolean locationEnabled = mController.isLocationEnabled();
state.visible = true;
- if (state.value != locationEnabled) {
- state.value = locationEnabled;
- final Resources res = mContext.getResources();
- final AnimationDrawable d = (AnimationDrawable) res.getDrawable(locationEnabled
- ? R.drawable.ic_qs_location_on
- : R.drawable.ic_qs_location_off);
- state.icon = d;
- mUiHandler.post(new Runnable() {
- @Override
- public void run() {
- d.start();
- }
- });
- }
+ state.value = locationEnabled;
if (locationEnabled) {
- if (state.icon == null) state.iconId = R.drawable.ic_qs_location_01;
+ state.iconId = R.drawable.ic_qs_location_on;
state.label = mContext.getString(R.string.quick_settings_location_label);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_location,
mContext.getString(R.string.accessibility_desc_on));
} else {
- if (state.icon == null) state.iconId = R.drawable.ic_qs_location_11;
- state.label = mContext.getString(R.string.quick_settings_location_off_label);
+ state.iconId = R.drawable.ic_qs_location_off;
+ state.label = mContext.getString(R.string.quick_settings_location_label);
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_location,
mContext.getString(R.string.accessibility_desc_off));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
index 20bbf8b..b981ed6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotificationsTile.java
@@ -22,7 +22,6 @@
import android.content.IntentFilter;
import android.media.AudioManager;
import android.util.Log;
-import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
@@ -49,8 +48,7 @@
@Override
public View createDetailView(Context context, ViewGroup root) {
- final Context themedContext = new ContextThemeWrapper(mContext, R.style.QSAccentTheme);
- final View v = LayoutInflater.from(themedContext).inflate(R.layout.qs_detail, root, false);
+ final View v = LayoutInflater.from(context).inflate(R.layout.qs_detail, root, false);
final TextView title = (TextView) v.findViewById(android.R.id.title);
title.setText(R.string.quick_settings_notifications_label);
final View close = v.findViewById(android.R.id.button1);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index a1e70b9..a236497 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -87,6 +87,7 @@
state.connected = wifiConnected;
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
+ state.filter = true;
final String signalContentDescription;
final Resources r = mContext.getResources();
if (wifiConnected) {
@@ -99,7 +100,7 @@
signalContentDescription = r.getString(R.string.accessibility_no_wifi);
} else {
state.iconId = R.drawable.ic_qs_wifi_no_network;
- state.label = r.getString(R.string.quick_settings_wifi_off_label);
+ state.label = r.getString(R.string.quick_settings_wifi_label);
signalContentDescription = r.getString(R.string.accessibility_wifi_off);
}
state.contentDescription = mContext.getString(
@@ -159,7 +160,7 @@
int mobileSignalIconId,
String mobileSignalContentDescriptionId, int dataTypeIconId,
boolean activityIn, boolean activityOut,
- String dataTypeContentDescriptionId, String description) {
+ String dataTypeContentDescriptionId, String description, boolean noSim) {
// noop
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
index 0cc09c8..e03c01c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -61,6 +61,11 @@
@Override
protected void onBootCompleted() {
+ if (mUseAlternateRecents) {
+ if (mAlternateRecents != null) {
+ mAlternateRecents.onBootCompleted();
+ }
+ }
mBootCompleted = true;
}
@@ -268,6 +273,13 @@
}
}
+ @Override
+ public void setCallback(Callbacks cb) {
+ if (mUseAlternateRecents) {
+ mAlternateRecents.setRecentsComponentCallback(cb);
+ }
+ }
+
/**
* Send broadcast only if BOOT_COMPLETED
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index ca9bb94..8861752 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -27,7 +27,6 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -35,16 +34,13 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
import com.android.systemui.R;
+import com.android.systemui.RecentsComponent;
+import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -129,8 +125,10 @@
final public static int MSG_TOGGLE_RECENTS = 6;
final public static int MSG_START_ENTER_ANIMATION = 7;
- final public static String EXTRA_ANIMATING_WITH_THUMBNAIL = "recents.animatingWithThumbnail";
- final public static String EXTRA_FROM_ALT_TAB = "recents.triggeredFromAltTab";
+ final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
+ final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
+ final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail";
+ final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration";
final public static String KEY_WINDOW_RECT = "recents.windowRect";
final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
@@ -138,7 +136,6 @@
final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect";
final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect";
-
final static int sMinToggleDelay = 425;
final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
@@ -146,12 +143,17 @@
final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
final static String sRecentsService = "com.android.systemui.recents.RecentsService";
+ static Bitmap sLastScreenshot;
+ static RecentsComponent.Callbacks sRecentsComponentCallbacks;
+
Context mContext;
SystemServicesProxy mSystemServicesProxy;
// Recents service binding
Messenger mService = null;
Messenger mMessenger;
+ RecentsMessageHandler mHandler;
+ boolean mBootCompleted = false;
boolean mServiceIsBound = false;
boolean mToggleRecentsUponServiceBound;
RecentsServiceConnection mConnection = new RecentsServiceConnection();
@@ -168,7 +170,8 @@
public AlternateRecentsComponent(Context context) {
mContext = context;
mSystemServicesProxy = new SystemServicesProxy(context);
- mMessenger = new Messenger(new RecentsMessageHandler());
+ mHandler = new RecentsMessageHandler();
+ mMessenger = new Messenger(mHandler);
}
public void onStart() {
@@ -180,6 +183,10 @@
bindToRecentsService(false);
}
+ public void onBootCompleted() {
+ mBootCompleted = true;
+ }
+
/** Shows the recents */
public void onShowRecents(boolean triggeredFromAltTab, View statusBarView) {
if (Console.Enabled) {
@@ -206,15 +213,19 @@
if (Console.Enabled) {
Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
}
- if (mServiceIsBound) {
- // Notify recents to close it
- try {
- Bundle data = new Bundle();
- Message msg = Message.obtain(null, MSG_HIDE_RECENTS, triggeredFromAltTab ? 1 : 0, 0);
- msg.setData(data);
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
+
+ if (mServiceIsBound && mBootCompleted) {
+ if (isRecentsTopMost(null)) {
+ // Notify recents to close it
+ try {
+ Bundle data = new Bundle();
+ Message msg = Message.obtain(null, MSG_HIDE_RECENTS,
+ triggeredFromAltTab ? 1 : 0, 0);
+ msg.setData(data);
+ mService.send(msg);
+ } catch (RemoteException re) {
+ re.printStackTrace();
+ }
}
}
}
@@ -276,7 +287,7 @@
/** Updates each of the task animation rects. */
void updateAnimationRects() {
- if (mServiceIsBound) {
+ if (mServiceIsBound && mBootCompleted) {
Resources res = mContext.getResources();
int statusBarHeight = res.getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
@@ -336,80 +347,6 @@
}
}
- /** Converts from the device rotation to the degree */
- float getDegreesForRotation(int value) {
- switch (value) {
- case Surface.ROTATION_90:
- return 360f - 90f;
- case Surface.ROTATION_180:
- return 360f - 180f;
- case Surface.ROTATION_270:
- return 360f - 270f;
- }
- return 0f;
- }
-
- /** Takes a screenshot of the surface */
- Bitmap takeScreenshot(Display display) {
- DisplayMetrics dm = new DisplayMetrics();
- display.getRealMetrics(dm);
- float[] dims = {dm.widthPixels, dm.heightPixels};
- float degrees = getDegreesForRotation(display.getRotation());
- boolean requiresRotation = (degrees > 0);
- if (requiresRotation) {
- // Get the dimensions of the device in its native orientation
- Matrix m = new Matrix();
- m.preRotate(-degrees);
- m.mapPoints(dims);
- dims[0] = Math.abs(dims[0]);
- dims[1] = Math.abs(dims[1]);
- }
- return SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
- }
-
- /** Creates the activity options for a thumbnail transition. */
- ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
- // Loading from thumbnail
- Bitmap thumbnail;
- Bitmap firstThumbnail = loadFirstTaskThumbnail();
- if (firstThumbnail != null) {
- // Create the thumbnail
- thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
- Bitmap.Config.ARGB_8888);
- int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
- Canvas c = new Canvas(thumbnail);
- c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
- new Rect(0, 0, taskRect.width(), taskRect.height()), null);
- c.setBitmap(null);
- // Recycle the old thumbnail
- firstThumbnail.recycle();
- } else {
- // Load the thumbnail from the screenshot if can't get one from the system
- WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Bitmap screenshot = takeScreenshot(display);
- if (screenshot != null) {
- Resources res = mContext.getResources();
- int size = Math.min(screenshot.getWidth(), screenshot.getHeight());
- int statusBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
- Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(thumbnail);
- c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight +
- size), new Rect(0, 0, taskRect.width(), taskRect.height()), null);
- c.setBitmap(null);
- // Recycle the temporary screenshot
- screenshot.recycle();
- } else {
- return null;
- }
- }
-
- return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, thumbnail,
- taskRect.left, taskRect.top, this);
- }
-
/** Returns whether the recents is currently running */
boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) {
SystemServicesProxy ssp = mSystemServicesProxy;
@@ -455,10 +392,12 @@
mService.send(msg);
// Time this path
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
+ }
} catch (RemoteException re) {
re.printStackTrace();
}
@@ -479,6 +418,68 @@
}
}
+ /**
+ * Creates the activity options for a unknown state->recents transition.
+ */
+ ActivityOptions getUnknownTransitionActivityOptions() {
+ // Reset the last screenshot
+ consumeLastScreenshot();
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_unknown_enter,
+ R.anim.recents_from_unknown_exit, mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for a home->recents transition.
+ */
+ ActivityOptions getHomeTransitionActivityOptions() {
+ // Reset the last screenshot
+ consumeLastScreenshot();
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_launcher_enter,
+ R.anim.recents_from_launcher_exit, mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for an app->recents transition. If this method sets the static
+ * screenshot, then we will use that for the transition.
+ */
+ ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
+ // Recycle the last screenshot
+ consumeLastScreenshot();
+
+ // Take the full screenshot
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ sLastScreenshot = mSystemServicesProxy.takeScreenshot();
+ if (sLastScreenshot != null) {
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_app_enter,
+ R.anim.recents_from_app_exit, mHandler, this);
+ }
+ }
+
+ // If the screenshot fails, then load the first task thumbnail and use that
+ Bitmap firstThumbnail = loadFirstTaskThumbnail();
+ if (firstThumbnail != null) {
+ // Create the new thumbnail for the animation down
+ // XXX: We should find a way to optimize this so we don't need to create a new bitmap
+ Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
+ Bitmap.Config.ARGB_8888);
+ int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
+ Canvas c = new Canvas(thumbnail);
+ c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
+ new Rect(0, 0, taskRect.width(), taskRect.height()), null);
+ c.setBitmap(null);
+ // Recycle the old thumbnail
+ firstThumbnail.recycle();
+ return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
+ thumbnail, taskRect.left, taskRect.top, this);
+ }
+
+ // If both the screenshot and thumbnail fails, then just fall back to the default transition
+ return getUnknownTransitionActivityOptions();
+ }
+
/** Starts the recents activity */
void startRecentsActivity(boolean isTopTaskHome) {
// If Recents is not the front-most activity and we should animate into it. If
@@ -496,7 +497,11 @@
// Try starting with a thumbnail transition
ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect);
if (opts != null) {
- startAlternateRecentsActivity(opts, true);
+ if (sLastScreenshot != null) {
+ startAlternateRecentsActivity(opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
+ } else {
+ startAlternateRecentsActivity(opts, EXTRA_FROM_APP_THUMBNAIL);
+ }
} else {
// Fall through below to the non-thumbnail transition
useThumbnailTransition = false;
@@ -505,25 +510,33 @@
// If there is no thumbnail transition, then just use a generic transition
if (!useThumbnailTransition) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_launcher_enter,
- R.anim.recents_from_launcher_exit);
- startAlternateRecentsActivity(opts, false);
+ if (Constants.DebugFlags.App.EnableHomeTransition) {
+ ActivityOptions opts = getHomeTransitionActivityOptions();
+ startAlternateRecentsActivity(opts, EXTRA_FROM_HOME);
+ } else {
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startAlternateRecentsActivity(opts, null);
+ }
}
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
+ }
mLastToggleTime = System.currentTimeMillis();
}
/** Starts the recents activity */
- void startAlternateRecentsActivity(ActivityOptions opts, boolean animatingWithThumbnail) {
+ void startAlternateRecentsActivity(ActivityOptions opts, String extraFlag) {
Intent intent = new Intent(sToggleRecentsAction);
intent.setClassName(sRecentsPackage, sRecentsActivity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(EXTRA_ANIMATING_WITH_THUMBNAIL, animatingWithThumbnail);
- intent.putExtra(EXTRA_FROM_ALT_TAB, mTriggeredFromAltTab);
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ if (extraFlag != null) {
+ intent.putExtra(extraFlag, true);
+ }
+ intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
if (opts != null) {
mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
UserHandle.USER_CURRENT));
@@ -532,6 +545,30 @@
}
}
+ /** Returns the last screenshot taken, this will be called by the RecentsActivity. */
+ public static Bitmap getLastScreenshot() {
+ return sLastScreenshot;
+ }
+
+ /** Recycles the last screenshot taken, this will be called by the RecentsActivity. */
+ public static void consumeLastScreenshot() {
+ if (sLastScreenshot != null) {
+ sLastScreenshot.recycle();
+ sLastScreenshot = null;
+ }
+ }
+
+ /** Sets the RecentsComponent callbacks. */
+ public void setRecentsComponentCallback(RecentsComponent.Callbacks cb) {
+ sRecentsComponentCallbacks = cb;
+ }
+
+ /** Notifies the callbacks that the visibility of Recents has changed. */
+ public static void notifyVisibilityChanged(boolean visible) {
+ if (sRecentsComponentCallbacks != null) {
+ sRecentsComponentCallbacks.onVisibilityChanged(visible);
+ }
+ }
/**** OnAnimationStartedListener Implementation ****/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 76e88a5..cd4d206 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -25,6 +25,10 @@
public static final boolean Verbose = false;
public static class App {
+ // Enables the home->Recents transition
+ public static final boolean EnableHomeTransition = false;
+ // Enables the screenshot app->Recents transition
+ public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
public static final boolean EnableTaskFiltering = false;
// Enables clipping of tasks against each other
@@ -52,8 +56,11 @@
public static class App {
public static final String TimeRecentsStartupKey = "startup";
public static final String TimeRecentsLaunchKey = "launchTask";
- public static final boolean TimeRecentsStartup = false;
- public static final boolean TimeRecentsLaunchTask = false;
+ public static final String TimeRecentsScreenshotTransitionKey = "screenshot";
+ public static final boolean TimeRecentsStartup = true;
+ public static final boolean TimeRecentsLaunchTask = true;
+ public static final boolean TimeRecentsScreenshotTransition = true;
+
public static final boolean RecentsComponent = false;
public static final boolean TaskDataLoader = false;
@@ -106,7 +113,7 @@
// The height of the peek space relative to the stack height
public static final float StackPeekHeightPct = 0.1f;
// The min scale of the last card in the peek area
- public static final float StackPeekMinScale = 0.9f;
+ public static final float StackPeekMinScale = 0.8f;
// The number of cards we see in the peek space
public static final int StackPeekNumCards = 3;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
new file mode 100644
index 0000000..247aa6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.os.Handler;
+
+/**
+ * A dozer is a class that fires a trigger after it falls asleep. You can occasionally poke it to
+ * wake it up, but it will fall asleep if left untouched.
+ */
+public class DozeTrigger {
+
+ Handler mHandler;
+
+ boolean mIsDozing;
+ boolean mHasTriggered;
+ int mDozeDurationSeconds;
+ Runnable mSleepRunnable;
+
+ // Sleep-runnable
+ Runnable mDozeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mSleepRunnable.run();
+ mIsDozing = false;
+ mHasTriggered = true;
+ }
+ };
+
+ public DozeTrigger(int dozeDurationSeconds, Runnable sleepRunnable) {
+ mHandler = new Handler();
+ mDozeDurationSeconds = dozeDurationSeconds;
+ mSleepRunnable = sleepRunnable;
+ }
+
+ /** Starts dozing. This also resets the trigger flag. */
+ public void startDozing() {
+ poke();
+ mHasTriggered = false;
+ }
+
+ /** Poke this dozer to wake it up for a little bit. */
+ public void poke() {
+ mHandler.removeCallbacks(mDozeRunnable);
+ mHandler.postDelayed(mDozeRunnable, mDozeDurationSeconds * 1000);
+ mIsDozing = true;
+ }
+
+ /** Returns whether we are dozing or not. */
+ public boolean isDozing() {
+ return mIsDozing;
+ }
+
+ /** Returns whether the trigger has fired at least once. */
+ public boolean hasTriggered() {
+ return mHasTriggered;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index df387c1..05c3252 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -22,24 +22,27 @@
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Pair;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.WindowManager;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.FullScreenTransitionView;
import com.android.systemui.recents.views.RecentsView;
+import com.android.systemui.recents.views.ViewAnimation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Set;
/** Our special app widget host */
class RecentsAppWidgetHost extends AppWidgetHost {
@@ -63,10 +66,16 @@
/* Activity */
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
- RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
+ RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
+ FullScreenTransitionView.FullScreenTransitionViewCallbacks {
+
FrameLayout mContainerView;
RecentsView mRecentsView;
View mEmptyView;
+ View mNavBarScrimView;
+ FullScreenTransitionView mFullScreenshotView;
+
+ RecentsConfiguration mConfig;
AppWidgetHost mAppWidgetHost;
AppWidgetProviderInfo mSearchAppWidgetInfo;
@@ -75,6 +84,19 @@
boolean mVisible;
boolean mTaskLaunched;
+ private static Method sPropertyMethod;
+ static {
+ try {
+ Class<?> c = Class.forName("android.view.GLES20Canvas");
+ sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
+ if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ }
+ }
+
// Broadcast receiver to handle messages from our RecentsService
BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -89,8 +111,15 @@
// Dismiss recents, launching the focused task
dismissRecentsIfVisible();
} else {
- // Otherwise, just finish the activity without launching any other activities
- finish();
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // Otherwise, just finish the activity without launching any other activities
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
}
} else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// Try and unfilter and filtered stacks
@@ -99,8 +128,10 @@
dismissRecentsIfVisible();
}
} else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
- // Try and start the enter animation
- mRecentsView.startOnEnterAnimation();
+ // Try and start the enter animation (or restart it on configuration changed)
+ mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ // Call our callback
+ onEnterAnimationTriggered();
}
}
};
@@ -109,18 +140,37 @@
BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // Mark recents as no longer visible
+ AlternateRecentsComponent.notifyVisibilityChanged(false);
+ // Finish without an animations
finish();
}
};
+ // A runnable to finish the Recents activity
+ Runnable mFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Mark recents as no longer visible
+ AlternateRecentsComponent.notifyVisibilityChanged(false);
+ // Finish with an animations
+ finish();
+ overridePendingTransition(R.anim.recents_to_launcher_enter,
+ R.anim.recents_to_launcher_exit);
+ }
+ };
+
/** Updates the set of recent tasks */
void updateRecentsTasks(Intent launchIntent) {
// Update the configuration based on the launch intent
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- config.launchedWithThumbnailAnimation = launchIntent.getBooleanExtra(
- AlternateRecentsComponent.EXTRA_ANIMATING_WITH_THUMBNAIL, false);
- config.launchedFromAltTab = launchIntent.getBooleanExtra(
- AlternateRecentsComponent.EXTRA_FROM_ALT_TAB, false);
+ mConfig.launchedFromHome = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_HOME, false);
+ mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false);
+ mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
+ mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
@@ -129,30 +179,23 @@
mRecentsView.setBSP(root);
}
+ // Hide the scrim by default when we enter recents
+ mNavBarScrimView.setVisibility(View.INVISIBLE);
+
// Add the default no-recents layout
if (stacks.size() == 1 && stacks.get(0).getTaskCount() == 0) {
mEmptyView.setVisibility(View.VISIBLE);
-
- // Dim the background even more
- WindowManager.LayoutParams wlp = getWindow().getAttributes();
- wlp.dimAmount = Constants.Values.Window.DarkBackgroundDim;
- getWindow().setAttributes(wlp);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
} else {
mEmptyView.setVisibility(View.GONE);
-
- // Un-dim the background
- WindowManager.LayoutParams wlp = getWindow().getAttributes();
- wlp.dimAmount = 0f;
- getWindow().setAttributes(wlp);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
+
+ // Dim the background
+ mRecentsView.setBackgroundColor(0x80000000);
}
/** Attempts to allocate and bind the search bar app widget */
void bindSearchBarAppWidget() {
if (Constants.DebugFlags.App.EnableSearchLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
// Reset the host view and widget info
@@ -160,7 +203,7 @@
mSearchAppWidgetInfo = null;
// Try and load the app widget id from the settings
- int appWidgetId = config.searchBarAppWidgetId;
+ int appWidgetId = mConfig.searchBarAppWidgetId;
if (appWidgetId >= 0) {
mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
if (mSearchAppWidgetInfo == null) {
@@ -190,7 +233,7 @@
}
// Save the app widget id into the settings
- config.updateSearchBarAppWidgetId(this, widgetInfo.first);
+ mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first);
mSearchAppWidgetInfo = widgetInfo.second;
}
}
@@ -200,8 +243,7 @@
/** Creates the search bar app widget view */
void addSearchBarAppWidgetView() {
if (Constants.DebugFlags.App.EnableSearchLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- int appWidgetId = config.searchBarAppWidgetId;
+ int appWidgetId = mConfig.searchBarAppWidgetId;
if (appWidgetId >= 0) {
if (Console.Enabled) {
Console.log(Constants.Log.App.SystemUIHandshake,
@@ -227,9 +269,19 @@
/** Dismisses recents if we are already visible and the intent is to toggle the recents view */
boolean dismissRecentsIfVisible() {
if (mVisible) {
- if (!mRecentsView.launchFocusedTask()) {
- if (!mRecentsView.launchFirstTask()) {
- finish();
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // If we have a focused task, then launch that task
+ if (!mRecentsView.launchFocusedTask()) {
+ // If there are any tasks, then launch the first task
+ if (!mRecentsView.launchFirstTask()) {
+ // We really shouldn't hit this, but if we do, just animate out (aka. finish)
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
}
}
return true;
@@ -251,7 +303,7 @@
// Initialize the loader and the configuration
RecentsTaskLoader.initialize(this);
- RecentsConfiguration.reinitialize(this);
+ mConfig = RecentsConfiguration.reinitialize(this);
// Initialize the widget host (the host id is static and does not change)
mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId, this);
@@ -269,19 +321,59 @@
// Create the empty view
LayoutInflater inflater = LayoutInflater.from(this);
mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false);
+ mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, mContainerView, false);
+ mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView = new FullScreenTransitionView(this, this);
+ mFullScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ }
mContainerView = new FrameLayout(this);
mContainerView.addView(mRecentsView);
mContainerView.addView(mEmptyView);
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mContainerView.addView(mFullScreenshotView);
+ }
+ mContainerView.addView(mNavBarScrimView);
setContentView(mContainerView);
// Update the recent tasks
updateRecentsTasks(getIntent());
+ // Prepare the screenshot transition if necessary
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
+ }
+
// Bind the search app widget when we first start up
bindSearchBarAppWidget();
// Add the search bar layout
addSearchBarAppWidgetView();
+
+ // Update if we are getting a configuration change
+ if (savedInstanceState != null) {
+ onConfigurationChange();
+ }
+
+ // XXX: Update the shadows
+ try {
+ sPropertyMethod.invoke(null, "ambientShadowStrength", String.valueOf(35f));
+ sPropertyMethod.invoke(null, "ambientRatio", String.valueOf(0.5f));
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ }
+
+ void onConfigurationChange() {
+ // Try and start the enter animation (or restart it on configuration changed)
+ mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ // Call our callback
+ onEnterAnimationTriggered();
}
@Override
@@ -300,11 +392,16 @@
// Initialize the loader and the configuration
RecentsTaskLoader.initialize(this);
- RecentsConfiguration.reinitialize(this);
+ mConfig = RecentsConfiguration.reinitialize(this);
// Update the recent tasks
updateRecentsTasks(intent);
+ // Prepare the screenshot transition if necessary
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
+ }
+
// Don't attempt to rebind the search bar widget, but just add the search bar layout
addSearchBarAppWidgetView();
}
@@ -318,8 +415,7 @@
super.onStart();
// Start listening for widget package changes if there is one bound
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.searchBarAppWidgetId >= 0) {
+ if (mConfig.searchBarAppWidgetId >= 0) {
mAppWidgetHost.startListening();
}
@@ -393,8 +489,7 @@
super.onStop();
// Stop listening for widget package changes if there was one bound
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.searchBarAppWidgetId >= 0) {
+ if (mConfig.searchBarAppWidgetId >= 0) {
mAppWidgetHost.stopListening();
}
@@ -432,28 +527,91 @@
}
@Override
- public void onBackPressed() {
- boolean interceptedByInfoPanelClose = false;
+ public void onUserInteraction() {
+ mRecentsView.onUserInteraction();
+ }
- // Unfilter any stacks
- if (!mRecentsView.unfilterFilteredStacks()) {
- super.onBackPressed();
+ @Override
+ public void onBackPressed() {
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // If we are currently filtering in any stacks, unfilter them first
+ if (!mRecentsView.unfilterFilteredStacks()) {
+ if (mConfig.launchedFromHome) {
+ // Just start the animation out of recents
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ } else {
+ // Otherwise, try and launch the first task
+ if (!mRecentsView.launchFirstTask()) {
+ // If there are no tasks, then just finish recents
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
+ }
+ }
+ }
+ }
+
+ public void onEnterAnimationTriggered() {
+ // Fade in the scrim
+ if (mConfig.hasNavBarScrim()) {
+ mNavBarScrimView.setVisibility(View.VISIBLE);
+ mNavBarScrimView.setAlpha(0f);
+ mNavBarScrimView.animate().alpha(1f)
+ .setStartDelay(mConfig.taskBarEnterAnimDelay)
+ .setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .withLayer()
+ .start();
}
}
@Override
- public void onTaskLaunching() {
+ public void onEnterAnimationComplete(boolean canceled) {
+ if (!canceled) {
+ // Reset the full screenshot transition view
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.reset();
+ }
+
+ // XXX: We should clean up the screenshot in this case as well, but it needs to happen
+ // after to animate up
+ }
+ // Recycle the full screen screenshot
+ AlternateRecentsComponent.consumeLastScreenshot();
+ }
+
+ @Override
+ public void onTaskLaunching(boolean isTaskInStackBounds) {
mTaskLaunched = true;
+
+ // Fade out the scrim
+ if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
+ mNavBarScrimView.animate().alpha(0f)
+ .setStartDelay(0)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .withLayer()
+ .start();
+ }
+
+ // Mark recents as no longer visible
+ AlternateRecentsComponent.notifyVisibilityChanged(false);
}
@Override
public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
- if (appWidgetId > -1 && appWidgetId == config.searchBarAppWidgetId) {
+ if (appWidgetId > -1 && appWidgetId == mConfig.searchBarAppWidgetId) {
// The search provider may have changed, so just delete the old widget and bind it again
ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
- config.updateSearchBarAppWidgetId(this, -1);
+ mConfig.updateSearchBarAppWidgetId(this, -1);
// Load the widget again
bindSearchBarAppWidget();
addSearchBarAppWidgetView();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 6391685..b1f3733 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -37,28 +37,39 @@
DisplayMetrics mDisplayMetrics;
- public Rect systemInsets = new Rect();
- public Rect displayRect = new Rect();
-
- boolean isLandscape;
- boolean transposeSearchLayoutWithOrientation;
- int searchBarAppWidgetId = -1;
-
+ /** Animations */
public float animationPxMovementPerSecond;
+ /** Interpolators */
public Interpolator fastOutSlowInInterpolator;
public Interpolator fastOutLinearInInterpolator;
public Interpolator linearOutSlowInInterpolator;
+ public Interpolator quintOutInterpolator;
+ /** Filtering */
public int filteringCurrentViewsMinAnimDuration;
public int filteringNewViewsMinAnimDuration;
- public int taskStackScrollDismissInfoPaneDistance;
- public int taskStackMaxDim;
- public float taskStackWidthPaddingPct;
- public int taskStackTopPaddingPx;
+ /** Insets */
+ public Rect systemInsets = new Rect();
+ public Rect displayRect = new Rect();
- public int taskViewInfoPaneAnimDuration;
+ /** Layout */
+ boolean isLandscape;
+ boolean transposeRecentsLayoutWithOrientation;
+
+ /** Search bar */
+ int searchBarAppWidgetId = -1;
+ public int searchBarSpaceHeightPx;
+
+ /** Task stack */
+ public int taskStackMaxDim;
+ public int taskStackTopPaddingPx;
+ public float taskStackWidthPaddingPct;
+
+ /** Task view animation and styles */
+ public int taskViewEnterFromHomeDuration;
+ public int taskViewEnterFromHomeDelay;
public int taskViewRemoveAnimDuration;
public int taskViewRemoveAnimTranslationXPx;
public int taskViewTranslationZMinPx;
@@ -67,20 +78,29 @@
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
- public int searchBarSpaceHeightPx;
-
+ /** Task bar colors */
public int taskBarViewDefaultBackgroundColor;
public int taskBarViewDefaultTextColor;
public int taskBarViewLightTextColor;
public int taskBarViewDarkTextColor;
public int taskBarViewHighlightColor;
+ /** Task bar animations */
public int taskBarEnterAnimDuration;
+ public int taskBarEnterAnimDelay;
public int taskBarExitAnimDuration;
+ public int taskBarDismissDozeDelaySeconds;
- public boolean launchedFromAltTab;
- public boolean launchedWithThumbnailAnimation;
+ /** Nav bar scrim */
+ public int navBarScrimEnterDuration;
+ /** Launch states */
+ public boolean launchedWithAltTab;
+ public boolean launchedFromAppWithThumbnail;
+ public boolean launchedFromAppWithScreenshot;
+ public boolean launchedFromHome;
+
+ /** Dev options */
public boolean developerOptionsEnabled;
/** Private constructor */
@@ -106,35 +126,54 @@
DisplayMetrics dm = res.getDisplayMetrics();
mDisplayMetrics = dm;
- isLandscape = res.getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE;
- transposeSearchLayoutWithOrientation =
- res.getBoolean(R.bool.recents_transpose_search_layout_with_orientation);
- if (Console.Enabled) {
- Console.log(Constants.Log.UI.MeasureAndLayout,
- "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
- Console.AnsiGreen);
- }
-
- displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
+ // Animations
animationPxMovementPerSecond =
res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
+
+ // Interpolators
+ fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
+ linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
+ quintOutInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.decelerate_quint);
+
+ // Filtering
filteringCurrentViewsMinAnimDuration =
res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
filteringNewViewsMinAnimDuration =
res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
- taskStackScrollDismissInfoPaneDistance = res.getDimensionPixelSize(
- R.dimen.recents_task_stack_scroll_dismiss_info_pane_distance);
- taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
+ // Insets
+ displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
+ // Layout
+ isLandscape = res.getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE;
+ transposeRecentsLayoutWithOrientation =
+ res.getBoolean(R.bool.recents_transpose_layout_with_orientation);
+
+ // Search bar
+ searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
+
+ // Update the search widget id
+ SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
+ searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
+
+ // Task stack
TypedValue widthPaddingPctValue = new TypedValue();
res.getValue(R.dimen.recents_stack_width_padding_percentage, widthPaddingPctValue, true);
taskStackWidthPaddingPct = widthPaddingPctValue.getFloat();
+ taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
- taskViewInfoPaneAnimDuration =
- res.getInteger(R.integer.recents_animate_task_view_info_pane_duration);
+ // Task view animation and styles
+ taskViewEnterFromHomeDuration =
+ res.getInteger(R.integer.recents_animate_task_enter_from_home_duration);
+ taskViewEnterFromHomeDelay =
+ res.getInteger(R.integer.recents_animate_task_enter_from_home_delay);
taskViewRemoveAnimDuration =
res.getInteger(R.integer.recents_animate_task_view_remove_duration);
taskViewRemoveAnimTranslationXPx =
@@ -148,8 +187,7 @@
taskViewShadowOutlineBottomInsetPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
- searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-
+ // Task bar colors
taskBarViewDefaultBackgroundColor =
res.getColor(R.color.recents_task_bar_default_background_color);
taskBarViewDefaultTextColor =
@@ -161,26 +199,30 @@
taskBarViewHighlightColor =
res.getColor(R.color.recents_task_bar_highlight_color);
+ // Task bar animations
taskBarEnterAnimDuration =
res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
+ taskBarEnterAnimDelay =
+ res.getInteger(R.integer.recents_animate_task_bar_enter_delay);
taskBarExitAnimDuration =
res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
+ taskBarDismissDozeDelaySeconds =
+ res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds);
- fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
- fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_linear_in);
- linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.linear_out_slow_in);
+ // Nav bar scrim
+ navBarScrimEnterDuration =
+ res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
// Check if the developer options are enabled
ContentResolver cr = context.getContentResolver();
developerOptionsEnabled = Settings.Global.getInt(cr,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
- // Update the search widget id
- SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
- searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.MeasureAndLayout,
+ "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
+ Console.AnsiGreen);
+ }
}
/** Updates the system insets */
@@ -199,15 +241,22 @@
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
- launchedFromAltTab = false;
- launchedWithThumbnailAnimation = false;
+ launchedWithAltTab = false;
+ launchedFromAppWithThumbnail = false;
+ launchedFromAppWithScreenshot = false;
+ launchedFromHome = false;
}
- /** Returns whether the search bar app widget exists */
+ /** Returns whether the search bar app widget exists. */
public boolean hasSearchBarAppWidget() {
return searchBarAppWidgetId >= 0;
}
+ /** Returns whether the nav bar scrim should be visible. */
+ public boolean hasNavBarScrim() {
+ return !transposeRecentsLayoutWithOrientation || !isLandscape;
+ }
+
/**
* Returns the task stack bounds in the current orientation. These bounds do not account for
* the system insets.
@@ -216,7 +265,7 @@
if (hasSearchBarAppWidget()) {
Rect searchBarBounds = new Rect();
getSearchBarBounds(width, height, searchBarBounds);
- if (isLandscape && transposeSearchLayoutWithOrientation) {
+ if (isLandscape && transposeRecentsLayoutWithOrientation) {
// In landscape, the search bar appears on the left, so shift the task rect right
taskStackBounds.set(searchBarBounds.width(), 0, width, height);
} else {
@@ -239,7 +288,7 @@
return;
}
- if (isLandscape && transposeSearchLayoutWithOrientation) {
+ if (isLandscape && transposeRecentsLayoutWithOrientation) {
// In landscape, the search bar appears on the left
searchBarSpaceBounds.set(0, 0, searchBarSpaceHeightPx, height);
} else {
@@ -247,15 +296,4 @@
searchBarSpaceBounds.set(0, 0, width, searchBarSpaceHeightPx);
}
}
-
- /** Converts from DPs to PXs */
- public int pxFromDp(float size) {
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- size, mDisplayMetrics));
- }
- /** Converts from SPs to PXs */
- public int pxFromSp(float size) {
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
- size, mDisplayMetrics));
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java
index 4e620b6..8bcc7f5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java
@@ -38,6 +38,7 @@
PackageCallbacks mCb;
List<ActivityManager.RecentTaskInfo> mTasks;
SystemServicesProxy mSsp;
+ boolean mRegistered;
public RecentsPackageMonitor(Context context) {
mSsp = new SystemServicesProxy(context);
@@ -46,13 +47,19 @@
/** Registers the broadcast receivers with the specified callbacks. */
public void register(Context context, PackageCallbacks cb) {
mCb = cb;
- register(context, Looper.getMainLooper(), false);
+ if (!mRegistered) {
+ register(context, Looper.getMainLooper(), false);
+ mRegistered = true;
+ }
}
/** Unregisters the broadcast receivers. */
@Override
public void unregister() {
- super.unregister();
+ if (mRegistered) {
+ super.unregister();
+ mRegistered = false;
+ }
mTasks.clear();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 0c2c11d..e554af7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -121,6 +121,7 @@
// Send a broadcast to hide recents
Intent intent = new Intent(RecentsService.ACTION_HIDE_RECENTS_ACTIVITY);
intent.setPackage(context.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (msg.arg1 != 0) {
intent.putExtra(RecentsService.EXTRA_TRIGGERED_FROM_ALT_TAB, true);
}
@@ -129,17 +130,21 @@
// Send a broadcast to toggle recents
Intent intent = new Intent(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY);
intent.setPackage(context.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
context.sendBroadcast(intent);
// Time this path
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+ }
} else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) {
// Send a broadcast to start the enter animation
Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION);
intent.setPackage(context.getPackageName());
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
context.sendBroadcast(intent);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index 4685186..dbcdb94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -478,7 +478,7 @@
// Load the thumbnail (if possible and not the foremost task, from the cache)
if (!isForemostTask) {
task.thumbnail = mThumbnailCache.get(task.key);
- if (task.thumbnail != null) {
+ if (task.thumbnail != null && !tasksToForceLoad.contains(task)) {
// Even though we get things from the cache, we should update them if
// they've changed in the bg
tasksToForceLoad.add(task);
@@ -489,6 +489,7 @@
Console.log(Constants.Log.App.TaskDataLoader,
"[RecentsTaskLoader|loadingTaskThumbnail]");
}
+
task.thumbnail = ssp.getTaskThumbnail(task.key.id);
if (task.thumbnail != null) {
task.thumbnail.setHasAlpha(false);
@@ -512,20 +513,6 @@
"" + (System.currentTimeMillis() - t1) + "ms");
}
- /*
- // Get all the stacks
- t1 = System.currentTimeMillis();
- List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
- Console.log(Constants.Log.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
- Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
- for (ActivityManager.StackInfo s : stackInfos) {
- Console.log(Constants.Log.App.TaskDataLoader, " [RecentsTaskLoader|stack]", s.toString());
- if (stacks.containsKey(s.stackId)) {
- stacks.get(s.stackId).setRect(s.bounds);
- }
- }
- */
-
// Start the task loader
mLoader.start(context);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
new file mode 100644
index 0000000..2f89e6d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.content.Context;
+
+/**
+ * A ref counted trigger that does some logic when the count is first incremented, or last
+ * decremented. Not thread safe as it's not currently needed.
+ */
+public class ReferenceCountedTrigger {
+
+ Context mContext;
+ int mCount;
+ Runnable mFirstIncRunnable;
+ Runnable mLastDecRunnable;
+ Runnable mErrorRunnable;
+
+ // Convenience runnables
+ Runnable mIncrementRunnable = new Runnable() {
+ @Override
+ public void run() {
+ increment();
+ }
+ };
+ Runnable mDecrementRunnable = new Runnable() {
+ @Override
+ public void run() {
+ decrement();
+ }
+ };
+
+ public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
+ Runnable lastDecRunnable, Runnable errorRunanable) {
+ mContext = context;
+ mFirstIncRunnable = firstIncRunnable;
+ mLastDecRunnable = lastDecRunnable;
+ mErrorRunnable = errorRunanable;
+ }
+
+ /** Increments the ref count */
+ public void increment() {
+ if (mCount == 0 && mFirstIncRunnable != null) {
+ mFirstIncRunnable.run();
+ }
+ mCount++;
+ }
+
+ /** Convenience method to increment this trigger as a runnable */
+ public Runnable incrementAsRunnable() {
+ return mIncrementRunnable;
+ }
+
+ /** Decrements the ref count */
+ public void decrement() {
+ mCount--;
+ if (mCount == 0 && mLastDecRunnable != null) {
+ mLastDecRunnable.run();
+ } else if (mCount < 0) {
+ if (mErrorRunnable != null) {
+ mErrorRunnable.run();
+ } else {
+ new Throwable("Invalid ref count").printStackTrace();
+ Console.logError(mContext, "Invalid ref count");
+ }
+ }
+ }
+
+ /** Convenience method to decrement this trigger as a runnable */
+ public Runnable decrementAsRunnable() {
+ return mDecrementRunnable;
+ }
+
+ /** Returns the current ref count */
+ public int getCount() {
+ return mCount;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 7a3ffb8..f532aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -30,13 +30,20 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Matrix;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DisplayMetrics;
import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Iterator;
@@ -54,6 +61,8 @@
IPackageManager mIpm;
UserManager mUm;
SearchManager mSm;
+ WindowManager mWm;
+ Display mDisplay;
String mRecentsPackage;
ComponentName mAssistComponent;
@@ -67,6 +76,8 @@
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mIpm = AppGlobals.getPackageManager();
mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+ mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = mWm.getDefaultDisplay();
mRecentsPackage = context.getPackageName();
// Resolve the assist intent
@@ -325,4 +336,13 @@
// Delete the app widget
host.deleteAppWidgetId(appWidgetId);
}
+
+ /**
+ * Takes a screenshot of the current surface.
+ */
+ public Bitmap takeScreenshot() {
+ DisplayInfo di = new DisplayInfo();
+ mDisplay.getDisplayInfo(di);
+ return SurfaceControl.screenshot(di.getNaturalWidth(), di.getNaturalHeight());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
new file mode 100644
index 0000000..ad2fa8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsConfiguration;
+
+
+/**
+ * The full screen transition view that gets animated down from the full screen into a task
+ * thumbnail view.
+ */
+public class FullScreenTransitionView extends FrameLayout {
+
+ /** The FullScreenTransitionView callbacks */
+ public interface FullScreenTransitionViewCallbacks {
+ void onEnterAnimationComplete(boolean canceled);
+ }
+
+ RecentsConfiguration mConfig;
+
+ FullScreenTransitionViewCallbacks mCb;
+
+ ImageView mScreenshotView;
+
+ Rect mClipRect = new Rect();
+
+ boolean mIsAnimating;
+ AnimatorSet mEnterAnimation;
+
+ public FullScreenTransitionView(Context context, FullScreenTransitionViewCallbacks cb) {
+ super(context);
+ mConfig = RecentsConfiguration.getInstance();
+ mCb = cb;
+ mScreenshotView = new ImageView(context);
+ mScreenshotView.setScaleType(ImageView.ScaleType.FIT_XY);
+ mScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ addView(mScreenshotView);
+ setClipTop(getClipTop());
+ setClipBottom(getClipBottom());
+ setWillNotDraw(false);
+ }
+
+ /** Sets the top clip */
+ public void setClipTop(int clip) {
+ mClipRect.top = clip;
+ postInvalidateOnAnimation();
+ }
+
+ /** Gets the top clip */
+ public int getClipTop() {
+ return mClipRect.top;
+ }
+
+ /** Sets the bottom clip */
+ public void setClipBottom(int clip) {
+ mClipRect.bottom = clip;
+ postInvalidateOnAnimation();
+ }
+
+ /** Gets the top clip */
+ public int getClipBottom() {
+ return mClipRect.bottom;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipRect(mClipRect);
+ super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+
+ /** Prepares the screenshot view for the transition into Recents */
+ public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
+ if (!mConfig.launchedFromAppWithScreenshot) return;
+
+ if (Console.Enabled) {
+ Console.logStartTracingTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey);
+ }
+
+ setClipTop(0);
+ setClipBottom(getMeasuredHeight());
+ setTranslationY(0f);
+ setScaleX(1f);
+ setScaleY(1f);
+ setVisibility(mConfig.launchedFromAppWithScreenshot ? View.VISIBLE : View.INVISIBLE);
+ if (screenshot != null) {
+ mScreenshotView.setImageBitmap(screenshot);
+ } else {
+ mScreenshotView.setImageDrawable(null);
+ }
+ }
+
+ /** Resets the transition view */
+ public void reset() {
+ setVisibility(View.INVISIBLE);
+ mScreenshotView.setImageDrawable(null);
+ }
+
+ /** Animates this view as it enters recents */
+ public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx,
+ final Runnable postAnimRunnable) {
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Starting");
+ }
+
+ // Cancel the current animation
+ if (mEnterAnimation != null) {
+ mEnterAnimation.removeAllListeners();
+ mEnterAnimation.cancel();
+ }
+
+ // Calculate the bottom clip
+ float scale = (float) ctx.taskRect.width() / getMeasuredWidth();
+ int translationY = -mConfig.systemInsets.top + ctx.stackRectSansPeek.top +
+ ctx.transform.translationY;
+ int clipBottom = mConfig.systemInsets.top + (int) (ctx.taskRect.height() / scale);
+
+ // Enable the HW Layers on the screenshot view
+ mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ // Compose the animation
+ mEnterAnimation = new AnimatorSet();
+ mEnterAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Notify any callbacks
+ mCb.onEnterAnimationComplete(false);
+ // Run the given post-anim runnable
+ postAnimRunnable.run();
+ // Mark that we are no longer animating
+ mIsAnimating = false;
+ // Disable the HW Layers on this view
+ setLayerType(View.LAYER_TYPE_NONE, null);
+
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Completed");
+ }
+ }
+ });
+ mEnterAnimation.setStartDelay(0);
+ mEnterAnimation.setDuration(475);
+ mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mEnterAnimation.playTogether(
+ ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top),
+ ObjectAnimator.ofInt(this, "clipBottom", clipBottom),
+ ObjectAnimator.ofFloat(this, "translationY", translationY),
+ ObjectAnimator.ofFloat(this, "scaleX", scale),
+ ObjectAnimator.ofFloat(this, "scaleY", scale)
+ );
+ mEnterAnimation.start();
+
+ mIsAnimating = true;
+ }
+
+ /** Animates this view back out of Recents if we were in the process of animating in. */
+ public boolean cancelAnimateOnEnterRecents(final Runnable postAnimRunnable) {
+ if (mIsAnimating) {
+ // Cancel the current animation
+ if (mEnterAnimation != null) {
+ mEnterAnimation.removeAllListeners();
+ mEnterAnimation.cancel();
+ }
+
+ // Compose the animation
+ mEnterAnimation = new AnimatorSet();
+ mEnterAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Notify any callbacks
+ mCb.onEnterAnimationComplete(true);
+ // Run the given post-anim runnable
+ postAnimRunnable.run();
+ // Mark that we are no longer animating
+ mIsAnimating = false;
+ // Disable the HW Layers on the screenshot view
+ mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
+ mEnterAnimation.setDuration(475);
+ mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mEnterAnimation.playTogether(
+ ObjectAnimator.ofInt(this, "clipTop", 0),
+ ObjectAnimator.ofInt(this, "clipBottom", getMeasuredHeight()),
+ ObjectAnimator.ofFloat(this, "translationY", 0f),
+ ObjectAnimator.ofFloat(this, "scaleX", 1f),
+ ObjectAnimator.ofFloat(this, "scaleY", 1f)
+ );
+ mEnterAnimation.start();
+
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 6005275..d019037 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -54,9 +54,12 @@
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
- public void onTaskLaunching();
+ public void onTaskLaunching(boolean isTaskInStackBounds);
}
+ RecentsConfiguration mConfig;
+ LayoutInflater mInflater;
+
// The space partitioning root of this container
SpaceNode mBSP;
// Whether there are any tasks
@@ -66,10 +69,9 @@
// Recents view callbacks
RecentsViewCallbacks mCb;
- LayoutInflater mInflater;
-
public RecentsView(Context context) {
super(context);
+ mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
setWillNotDraw(false);
}
@@ -159,17 +161,39 @@
}
/** Requests all task stacks to start their enter-recents animation */
- public void startOnEnterAnimation() {
+ public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnEnterAnimation();
+ stackView.startOnEnterAnimation(ctx);
}
}
}
+ /** Requests all task stacks to start their exit-recents animation */
+ public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ // Handle the case when there are no views by incrementing and decrementing after all
+ // animations are started.
+ ctx.postAnimationTrigger.increment();
+
+ if (Constants.DebugFlags.App.EnableHomeTransition) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView) {
+ TaskStackView stackView = (TaskStackView) child;
+ stackView.startOnExitAnimation(ctx);
+ }
+ }
+ }
+
+ // Handle the case when there are no views by incrementing and decrementing after all
+ // animations are started.
+ ctx.postAnimationTrigger.decrement();
+ }
+
/** Adds the search bar */
public void setSearchBar(View searchBar) {
// Create the search bar (and hide it if we have no recent tasks)
@@ -211,10 +235,9 @@
}
// Get the search bar bounds and measure the search bar layout
- RecentsConfiguration config = RecentsConfiguration.getInstance();
if (mSearchBar != null) {
Rect searchBarSpaceBounds = new Rect();
- config.getSearchBarBounds(width, height - config.systemInsets.top, searchBarSpaceBounds);
+ mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds);
mSearchBar.measure(
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
@@ -225,9 +248,9 @@
// In addition, we give it the full height, not including the top inset or search bar space,
// since we want the tasks to render under the navigation buttons in portrait.
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(width, height, taskStackBounds);
- int childWidth = width - config.systemInsets.right;
- int childHeight = taskStackBounds.height() - config.systemInsets.top;
+ mConfig.getTaskStackBounds(width, height, taskStackBounds);
+ int childWidth = width - mConfig.systemInsets.right;
+ int childHeight = taskStackBounds.height() - mConfig.systemInsets.top;
// Measure each TaskStackView
int childCount = getChildCount();
@@ -255,23 +278,22 @@
}
// Get the search bar bounds so that we lay it out
- RecentsConfiguration config = RecentsConfiguration.getInstance();
if (mSearchBar != null) {
Rect searchBarSpaceBounds = new Rect();
- config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
- mSearchBar.layout(config.systemInsets.left + searchBarSpaceBounds.left,
- config.systemInsets.top + searchBarSpaceBounds.top,
- config.systemInsets.left + mSearchBar.getMeasuredWidth(),
- config.systemInsets.top + mSearchBar.getMeasuredHeight());
+ mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
+ mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left,
+ mConfig.systemInsets.top + searchBarSpaceBounds.top,
+ mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(),
+ mConfig.systemInsets.top + mSearchBar.getMeasuredHeight());
}
// We offset the stack view by the left inset (if any), but lay it out under the search bar.
// In addition, we offset our stack views by the top inset and search bar height, but not
// the bottom insets because we want it to render under the navigation buttons.
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
- left += config.systemInsets.left;
- top += config.systemInsets.top + taskStackBounds.top;
+ mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
+ left += mConfig.systemInsets.left;
+ top += mConfig.systemInsets.top + taskStackBounds.top;
// Layout each child
// XXX: Based on the space node for that task view
@@ -285,6 +307,20 @@
}
}
+ /** Notifies each task view of the user interaction. */
+ public void onUserInteraction() {
+ // Get the first stack view
+ TaskStackView stackView = null;
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView) {
+ stackView = (TaskStackView) child;
+ stackView.onUserInteraction();
+ }
+ }
+ }
+
/** Focuses the next task in the first stack view */
public void focusNextTask(boolean forward) {
// Get the first stack view
@@ -320,8 +356,7 @@
}
// Update the configuration with the latest system insets and trigger a relayout
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- config.updateSystemInsets(insets.getSystemWindowInsets());
+ mConfig.updateSystemInsets(insets.getSystemWindowInsets());
requestLayout();
return insets.consumeSystemWindowInsets(false, false, false, true);
@@ -351,12 +386,21 @@
final TaskStack stack, final Task task) {
// Notify any callbacks of the launching of a new task
if (mCb != null) {
- mCb.onTaskLaunching();
+ boolean isTaskInStackBounds = false;
+ if (stackView != null && tv != null) {
+ isTaskInStackBounds = stackView.isTaskInStackBounds(tv);
+ }
+ mCb.onTaskLaunching(isTaskInStackBounds);
}
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
+ }
+
TaskViewTransform transform;
View sourceView = tv;
int offsetX = 0;
@@ -366,11 +410,10 @@
// If there is no actual task view, then use the stack view as the source view
// and then offset to the expected transform rect, but bound this to just
// outside the display rect (to ensure we don't animate from too far away)
- RecentsConfiguration config = RecentsConfiguration.getInstance();
sourceView = stackView;
transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
offsetX = transform.rect.left;
- offsetY = Math.min(transform.rect.top, config.displayRect.height());
+ offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
} else {
transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
}
@@ -418,19 +461,23 @@
onTaskRemoved(task);
}
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
+ }
}
};
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
+ }
// Launch the app right away if there is no task view, otherwise, animate the icon out first
if (tv == null) {
post(launchRunnable);
} else {
- tv.animateOnLeavingRecents(launchRunnable);
+ tv.animateOnLaunchingTask(launchRunnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index c10ddd1..aae345b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -22,8 +22,10 @@
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -36,6 +38,9 @@
/* The task bar view */
class TaskBarView extends FrameLayout {
+
+ RecentsConfiguration mConfig;
+
Task mTask;
ImageView mDismissButton;
@@ -61,6 +66,7 @@
public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
setWillNotDraw(false);
// Load the dismiss resources
@@ -70,11 +76,10 @@
// Configure the highlight paint
if (sHighlightPaint == null) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
sHighlightPaint = new Paint();
sHighlightPaint.setStyle(Paint.Style.STROKE);
- sHighlightPaint.setStrokeWidth(config.taskViewHighlightPx);
- sHighlightPaint.setColor(config.taskBarViewHighlightColor);
+ sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
+ sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
sHighlightPaint.setAntiAlias(true);
}
@@ -90,11 +95,9 @@
@Override
protected void onDraw(Canvas canvas) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
-
// Draw the highlight at the top edge (but put the bottom edge just out of view)
- float offset = config.taskViewHighlightPx / 2f;
- float radius = config.taskViewRoundedCornerRadiusPx;
+ float offset = mConfig.taskViewHighlightPx / 2f;
+ float radius = mConfig.taskViewRoundedCornerRadiusPx;
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
}
@@ -102,8 +105,7 @@
/** Synchronizes this bar view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
TaskViewTransform toTransform, int duration) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (duration > 0) {
+ if (duration > 0 && (mDismissButton.getVisibility() == View.VISIBLE)) {
if (animateFromTransform != null) {
mDismissButton.setAlpha(animateFromTransform.dismissAlpha);
}
@@ -111,18 +113,16 @@
.alpha(toTransform.dismissAlpha)
.setStartDelay(0)
.setDuration(duration)
- .setInterpolator(config.fastOutSlowInInterpolator)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
.withLayer()
.start();
} else {
mDismissButton.setAlpha(toTransform.dismissAlpha);
}
- mDismissButton.invalidate();
}
/** Binds the bar view to the task */
void rebindToTask(Task t, boolean animate) {
- RecentsConfiguration configuration = RecentsConfiguration.getInstance();
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
@@ -137,12 +137,12 @@
if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
setBackgroundColor(tint);
mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
- configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor));
+ mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor));
mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
mLightDismissDrawable, mDarkDismissDrawable));
} else {
- setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
- mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor);
+ setBackgroundColor(mConfig.taskBarViewDefaultBackgroundColor);
+ mActivityDescription.setTextColor(mConfig.taskBarViewDefaultTextColor);
}
if (animate) {
// XXX: Investigate how expensive it will be to create a second bitmap and crossfade
@@ -155,4 +155,74 @@
mApplicationIcon.setImageDrawable(null);
mActivityDescription.setText("");
}
+
+ /** Prepares this task view for the enter-recents animations. This is called earlier in the
+ * first layout because the actual animation into recents may take a long time. */
+ public void prepareAnimateEnterRecents() {
+ setVisibility(View.INVISIBLE);
+ }
+
+ /** Animates this task bar as it enters recents */
+ public void animateOnEnterRecents(int delay) {
+ // Animate the task bar of the first task view
+ setVisibility(View.VISIBLE);
+ setTranslationY(-getMeasuredHeight());
+ animate()
+ .translationY(0)
+ .setStartDelay(delay)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskBarEnterAnimDuration)
+ .withLayer()
+ .start();
+ }
+
+ /** Animates this task bar as it exits recents */
+ public void animateOnLaunchingTask(final Runnable r) {
+ // Animate the task bar out of the first task view
+ animate()
+ .translationY(-getMeasuredHeight())
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutLinearInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .withLayer()
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ post(r);
+ }
+ })
+ .start();
+ }
+
+ /** Animates this task bar if the user does not interact with the stack after a certain time. */
+ public void animateOnNoUserInteraction() {
+ mDismissButton.setVisibility(View.VISIBLE);
+ mDismissButton.setAlpha(0f);
+ mDismissButton.animate()
+ .alpha(1f)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutLinearInInterpolator)
+ .setDuration(mConfig.taskBarEnterAnimDuration)
+ .withLayer()
+ .start();
+ }
+
+ /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+ public void setOnNoUserInteraction() {
+ if (mDismissButton.getVisibility() != View.VISIBLE) {
+ mDismissButton.animate().cancel();
+ mDismissButton.setVisibility(View.VISIBLE);
+ mDismissButton.setAlpha(1f);
+ }
+ }
+
+ /** Enable the hw layers on this task view */
+ void enableHwLayers() {
+ mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ }
+
+ /** Disable the hw layers on this task view */
+ void disableHwLayers() {
+ mDismissButton.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 053f122..b503c36 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -38,9 +38,11 @@
import com.android.systemui.R;
import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.DozeTrigger;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsPackageMonitor;
import com.android.systemui.recents.RecentsTaskLoader;
+import com.android.systemui.recents.ReferenceCountedTrigger;
import com.android.systemui.recents.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -62,10 +64,14 @@
public void onTaskRemoved(Task t);
}
+ RecentsConfiguration mConfig;
+
TaskStack mStack;
TaskStackViewTouchHandler mTouchHandler;
TaskStackViewCallbacks mCb;
ViewPool<TaskView, Task> mViewPool;
+ ArrayList<TaskViewTransform> mTaskTransforms = new ArrayList<TaskViewTransform>();
+ DozeTrigger mDozeTrigger;
// The various rects that define the stack view
Rect mRect = new Rect();
@@ -83,11 +89,12 @@
ObjectAnimator mScrollAnimator;
// Optimizations
- int mHwLayersRefCount;
+ ReferenceCountedTrigger mHwLayersTrigger;
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
boolean mAwaitingFirstLayout = true;
boolean mStartEnterAnimationRequestedAfterLayout;
+ ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
int[] mTmpVisibleRange = new int[2];
Rect mTmpRect = new Rect();
Rect mTmpRect2 = new Rect();
@@ -95,12 +102,51 @@
public TaskStackView(Context context, TaskStack stack) {
super(context);
+ mConfig = RecentsConfiguration.getInstance();
mStack = stack;
mStack.setCallbacks(this);
mScroller = new OverScroller(context);
mTouchHandler = new TaskStackViewTouchHandler(context, this);
mViewPool = new ViewPool<TaskView, Task>(context, this);
mInflater = LayoutInflater.from(context);
+ mDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
+ @Override
+ public void run() {
+ // Show the task bar dismiss buttons
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.animateOnNoUserInteraction();
+ }
+ }
+ });
+ mHwLayersTrigger = new ReferenceCountedTrigger(getContext(), new Runnable() {
+ @Override
+ public void run() {
+ // Enable hw layers on each of the children
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.enableHwLayers();
+ }
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ // Disable hw layers on each of the children
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.disableHwLayers();
+ }
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ new Throwable("Invalid hw layers ref count").printStackTrace();
+ Console.logError(getContext(), "Invalid HW layers ref count");
+ }
+ });
}
/** Sets the callbacks */
@@ -118,7 +164,7 @@
"[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow);
}
if (!mStackViewsDirty) {
- invalidate();
+ invalidate(mStackRect);
}
if (mAwaitingFirstLayout) {
// Skip the animation if we are awaiting first layout
@@ -156,14 +202,16 @@
float boundedT = Math.max(t, -(numPeekCards + 1));
// Set the scale relative to its position
+ int numFrontScaledCards = 3;
float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
float scaleRange = 1f - minScale;
- float scaleInc = scaleRange / numPeekCards;
- float scale = Math.max(minScale, Math.min(1f, 1f + (boundedT * scaleInc)));
+ float scaleInc = scaleRange / (numPeekCards + numFrontScaledCards);
+ float scale = Math.max(minScale, Math.min(1f, minScale +
+ ((boundedT + (numPeekCards + 1)) * scaleInc)));
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
transform.scale = scale;
- // Set the translation
+ // Set the y translation
if (boundedT < 0f) {
transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
numPeekCards) * peekHeight - scaleYOffset);
@@ -171,6 +219,11 @@
transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
}
+ // Set the z translation
+ int minZ = mConfig.taskViewTranslationZMinPx;
+ int incZ = mConfig.taskViewTranslationZIncrementPx;
+ transform.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
+
// Set the alphas
transform.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
@@ -190,16 +243,18 @@
/**
* Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
*/
- private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
- int stackScroll,
- int[] visibleRangeOut,
- boolean boundTranslationsToRect) {
+ private void updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
+ ArrayList<Task> tasks,
+ int stackScroll,
+ int[] visibleRangeOut,
+ boolean boundTranslationsToRect) {
// XXX: Optimization: Use binary search to find the visible range
- ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
int taskCount = tasks.size();
int firstVisibleIndex = -1;
int lastVisibleIndex = -1;
+ taskTransforms.clear();
+ taskTransforms.ensureCapacity(taskCount);
for (int i = 0; i < taskCount; i++) {
TaskViewTransform transform = getStackTransform(i, stackScroll);
taskTransforms.add(transform);
@@ -218,6 +273,19 @@
visibleRangeOut[0] = firstVisibleIndex;
visibleRangeOut[1] = lastVisibleIndex;
}
+ }
+
+ /**
+ * Gets the stack transforms of a list of tasks, and returns the visible range of tasks. This
+ * call is less optimal than calling updateStackTransforms directly.
+ */
+ private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
+ int stackScroll,
+ int[] visibleRangeOut,
+ boolean boundTranslationsToRect) {
+ ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
+ updateStackTransforms(taskTransforms, tasks, stackScroll, visibleRangeOut,
+ boundTranslationsToRect);
return taskTransforms;
}
@@ -237,14 +305,13 @@
int[] visibleRange = mTmpVisibleRange;
int stackScroll = getStackScroll();
ArrayList<Task> tasks = mStack.getTasks();
- ArrayList<TaskViewTransform> taskTransforms = getStackTransforms(tasks, stackScroll,
- visibleRange, false);
+ updateStackTransforms(mTaskTransforms, tasks, stackScroll, visibleRange, false);
// Update the visible state of all the tasks
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
- TaskViewTransform transform = taskTransforms.get(i);
+ TaskViewTransform transform = mTaskTransforms.get(i);
TaskView tv = getChildViewForTask(task);
if (transform.visible) {
@@ -273,10 +340,10 @@
TaskView tv = (TaskView) getChildAt(i);
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
- if (taskIndex < 0 || !taskTransforms.get(taskIndex).visible) {
+ if (taskIndex < 0 || !mTaskTransforms.get(taskIndex).visible) {
mViewPool.returnViewToPool(tv);
} else {
- tv.updateViewPropertiesToTaskTransform(null, taskTransforms.get(taskIndex),
+ tv.updateViewPropertiesToTaskTransform(null, mTaskTransforms.get(taskIndex),
mStackViewsAnimationDuration);
}
}
@@ -353,7 +420,7 @@
mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
curScroll, 250));
- mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().fastOutSlowInInterpolator);
+ mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
@@ -430,6 +497,13 @@
return getScrollAmountOutOfBounds(getStackScroll()) != 0;
}
+ /** Returns whether the task view is in the stack bounds or not */
+ boolean isTaskInStackBounds(TaskView tv) {
+ Rect r = new Rect();
+ tv.getHitRect(r);
+ return r.bottom <= mRect.bottom;
+ }
+
/** Updates the min and max virtual scroll bounds */
void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
// Compute the min and max scroll values
@@ -528,48 +602,31 @@
/** Enables the hw layers and increments the hw layer requirement ref count */
void addHwLayersRefCount(String reason) {
if (Console.Enabled) {
+ int refCount = mHwLayersTrigger.getCount();
Console.log(Constants.Log.UI.HwLayers,
"[TaskStackView|addHwLayersRefCount] refCount: " +
- mHwLayersRefCount + "->" + (mHwLayersRefCount + 1) + " " + reason);
+ refCount + "->" + (refCount + 1) + " " + reason);
}
- if (mHwLayersRefCount == 0) {
- // Enable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.enableHwLayers();
- }
- }
- mHwLayersRefCount++;
+ mHwLayersTrigger.increment();
}
/** Decrements the hw layer requirement ref count and disables the hw layers when we don't
need them anymore. */
void decHwLayersRefCount(String reason) {
if (Console.Enabled) {
+ int refCount = mHwLayersTrigger.getCount();
Console.log(Constants.Log.UI.HwLayers,
"[TaskStackView|decHwLayersRefCount] refCount: " +
- mHwLayersRefCount + "->" + (mHwLayersRefCount - 1) + " " + reason);
+ refCount + "->" + (refCount - 1) + " " + reason);
}
- mHwLayersRefCount--;
- if (mHwLayersRefCount == 0) {
- // Disable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.disableHwLayers();
- }
- } else if (mHwLayersRefCount < 0) {
- new Throwable("Invalid hw layers ref count").printStackTrace();
- Console.logError(getContext(), "Invalid HW layers ref count");
- }
+ mHwLayersTrigger.decrement();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
setStackScroll(mScroller.getCurrY());
- invalidate();
+ invalidate(mStackRect);
// If we just finished scrolling, then disable the hw layers
if (mScroller.isFinished()) {
@@ -601,7 +658,6 @@
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (Constants.DebugFlags.App.EnableTaskStackClipping) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
TaskView tv = (TaskView) child;
TaskView nextTv = null;
TaskView tmpTv = null;
@@ -617,13 +673,13 @@
}
// Clip against the next view (if we aren't animating its alpha)
- if (nextTv != null && nextTv.getAlpha() == 1f) {
+ if (nextTv != null) {
Rect curRect = tv.getClippingRect(mTmpRect);
Rect nextRect = nextTv.getClippingRect(mTmpRect2);
// The hit rects are relative to the task view, which needs to be offset by
// the system bar height
- curRect.offset(0, config.systemInsets.top);
- nextRect.offset(0, config.systemInsets.top);
+ curRect.offset(0, mConfig.systemInsets.top);
+ nextRect.offset(0, mConfig.systemInsets.top);
// Compute the clip region
Region clipRegion = new Region();
clipRegion.op(curRect, Region.Op.UNION);
@@ -645,7 +701,6 @@
// Note: We let the stack view be the full height because we want the cards to go under the
// navigation bar if possible. However, the stack rects which we use to calculate
// max scroll, etc. need to take the nav bar into account
- RecentsConfiguration config = RecentsConfiguration.getInstance();
// Compute the stack rects
mRect.set(0, 0, width, height);
@@ -653,8 +708,8 @@
mStackRect.left += insetLeft;
mStackRect.bottom -= insetBottom;
- int widthPadding = (int) (config.taskStackWidthPaddingPct * mStackRect.width());
- int heightPadding = config.taskStackTopPaddingPx;
+ int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
+ int heightPadding = mConfig.taskStackTopPaddingPx;
if (Constants.DebugFlags.App.EnableSearchLayout) {
mStackRect.top += heightPadding;
mStackRect.left += widthPadding;
@@ -692,10 +747,9 @@
}
// Compute our stack/task rects
- RecentsConfiguration config = RecentsConfiguration.getInstance();
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(width, height, taskStackBounds);
- computeRects(width, height, taskStackBounds.left, config.systemInsets.bottom);
+ mConfig.getTaskStackBounds(width, height, taskStackBounds);
+ computeRects(width, height, taskStackBounds.left, mConfig.systemInsets.bottom);
// Debug logging
if (Constants.Log.UI.MeasureAndLayout) {
@@ -753,47 +807,69 @@
}
if (mAwaitingFirstLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
-
- // Update the focused task index to be the next item to the top task
- if (config.launchedFromAltTab) {
- focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
- }
-
- // Prepare the first view for its enter animation
- if (config.launchedWithThumbnailAnimation) {
- TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
- if (tv != null) {
- tv.prepareAnimateOnEnterRecents();
- }
- }
-
// Mark that we have completely the first layout
mAwaitingFirstLayout = false;
+ // Start dozing
+ mDozeTrigger.startDozing();
+
+ // Prepare the first view for its enter animation
+ int offsetTopAlign = -mTaskRect.top;
+ int offscreenY = mRect.bottom - (mTaskRect.top - mRect.top);
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.prepareAnimateEnterRecents((i == (getChildCount() - 1)), offsetTopAlign,
+ offscreenY, mTaskRect);
+ }
+
// If the enter animation started already and we haven't completed a layout yet, do the
// enter animation now
if (mStartEnterAnimationRequestedAfterLayout) {
- startOnEnterAnimation();
+ startOnEnterAnimation(mStartEnterAnimationContext);
+ mStartEnterAnimationRequestedAfterLayout = false;
+ mStartEnterAnimationContext = null;
+ }
+
+ // Update the focused task index to be the next item to the top task
+ if (mConfig.launchedWithAltTab) {
+ focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
}
}
}
/** Requests this task stacks to start it's enter-recents animation */
- public void startOnEnterAnimation() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (!config.launchedWithThumbnailAnimation) return;
-
+ public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// If we are still waiting to layout, then just defer until then
if (mAwaitingFirstLayout) {
mStartEnterAnimationRequestedAfterLayout = true;
+ mStartEnterAnimationContext = ctx;
return;
}
- // Animate the task bar of the first task view
- TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
- if (tv != null) {
- tv.animateOnEnterRecents();
+ // Animate all the task views into view
+ ctx.taskRect = mTaskRect;
+ ctx.stackRectSansPeek = mStackRectSansPeek;
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ TaskViewTransform transform = getStackTransform(mStack.indexOfTask(tv.getTask()),
+ getStackScroll());
+ ctx.stackViewIndex = i;
+ ctx.stackViewCount = childCount;
+ ctx.isFrontMost = (i == (getChildCount() - 1));
+ ctx.transform = transform;
+ tv.animateOnEnterRecents(ctx);
+ }
+ }
+
+ /** Requests this task stacks to start it's exit-recents animation. */
+ public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ // Animate all the task views into view
+ ctx.offscreenTranslationY = mRect.bottom - (mTaskRect.top - mRect.top);
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.animateOnExitRecents(ctx);
}
}
@@ -807,6 +883,15 @@
return isTransformedTouchPointInView(x, y, child, null);
}
+ /** Pokes the dozer on user interaction. */
+ void onUserInteraction() {
+ // If the dozer is not running, then either we have not yet laid out, or it has already
+ // fallen asleep, so just let it rest.
+ if (mDozeTrigger.isDozing()) {
+ mDozeTrigger.poke();
+ }
+ }
+
/**** TaskStackCallbacks Implementation ****/
@Override
@@ -856,8 +941,7 @@
ArrayList<TaskViewTransform> curTaskTransforms,
ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms,
HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut,
- ArrayList<TaskView> childrenToRemoveOut,
- RecentsConfiguration config) {
+ ArrayList<TaskView> childrenToRemoveOut) {
// Animate all of the existing views out of view (if they are not in the visible range in
// the new stack) or to their final positions in the new stack
int movement = 0;
@@ -887,7 +971,7 @@
childViewTransformsOut.put(tv, new Pair(0, toTransform));
}
return Utilities.calculateTranslationAnimationDuration(movement,
- config.filteringCurrentViewsMinAnimDuration);
+ mConfig.filteringCurrentViewsMinAnimDuration);
}
/**
@@ -896,8 +980,7 @@
*/
int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks,
ArrayList<TaskViewTransform> taskTransforms,
- HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut,
- RecentsConfiguration config) {
+ HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut) {
int offset = 0;
int movement = 0;
int taskCount = tasks.size();
@@ -927,7 +1010,7 @@
}
}
return Utilities.calculateTranslationAnimationDuration(movement,
- config.filteringNewViewsMinAnimDuration);
+ mConfig.filteringNewViewsMinAnimDuration);
}
/** Orchestrates the animations of the current child views and any new views. */
@@ -935,22 +1018,20 @@
ArrayList<TaskViewTransform> curTaskTransforms,
final ArrayList<Task> tasks,
final ArrayList<TaskViewTransform> taskTransforms) {
- final RecentsConfiguration config = RecentsConfiguration.getInstance();
-
// Calculate the transforms to animate out all the existing views if they are not in the
// new visible range (or to their final positions in the stack if they are)
final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>();
final HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransforms =
new HashMap<TaskView, Pair<Integer, TaskViewTransform>>();
int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks,
- taskTransforms, childViewTransforms, childrenToRemove, config);
+ taskTransforms, childViewTransforms, childrenToRemove);
// If all the current views are in the visible range of the new stack, then don't wait for
// views to animate out and animate all the new views into their place
final boolean unifyNewViewAnimation = childrenToRemove.isEmpty();
if (unifyNewViewAnimation) {
int inDuration = getEnterTransformsForFilterAnimation(tasks, taskTransforms,
- childViewTransforms, config);
+ childViewTransforms);
duration = Math.max(duration, inDuration);
}
@@ -974,7 +1055,7 @@
// For views that are not already visible, animate them in
childViewTransforms.clear();
int duration = getEnterTransformsForFilterAnimation(tasks,
- taskTransforms, childViewTransforms, config);
+ taskTransforms, childViewTransforms);
for (final TaskView tv : childViewTransforms.keySet()) {
Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv);
tv.animate().setStartDelay(t.first);
@@ -1096,6 +1177,11 @@
// but just in case, re-enable it here
tv.setClipViewInStack(true);
+ // If the doze trigger has already fired, then update the state for this task view
+ if (mDozeTrigger.hasTriggered()) {
+ tv.setOnNoUserInteraction();
+ }
+
// Add/attach the view to the hierarchy
if (Console.Enabled) {
Console.log(Constants.Log.ViewPool.PoolCallbacks, " [TaskStackView|insertIndex]",
@@ -1112,7 +1198,7 @@
}
// Enable hw layers on this view if hw layers are enabled on the stack
- if (mHwLayersRefCount > 0) {
+ if (mHwLayersTrigger.getCount() > 0) {
tv.enableHwLayers();
}
}
@@ -1181,7 +1267,6 @@
@Override
public void onComponentRemoved(Set<ComponentName> cns) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
// For other tasks, just remove them directly if they no longer exist
ArrayList<Task> tasks = mStack.getTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
@@ -1487,7 +1572,7 @@
mSv.mMinScroll, mSv.mMaxScroll,
0, overscrollRange);
// Invalidate to kick off computeScroll
- mSv.invalidate();
+ mSv.invalidate(mSv.mStackRect);
} else if (mSv.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
// XXX: Make this animation a function of the velocity OR distance
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index ffa181d..7376255 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.views;
+import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -28,9 +29,11 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewParent;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
@@ -45,10 +48,10 @@
public void onTaskAppInfoClicked(TaskView tv);
public void onTaskFocused(TaskView tv);
public void onTaskDismissed(TaskView tv);
-
- // public void onTaskViewReboundToTask(TaskView tv, Task t);
}
+ RecentsConfiguration mConfig;
+
int mDim;
int mMaxDim;
TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
@@ -59,11 +62,21 @@
boolean mClipViewInStack;
Point mLastTouchDown = new Point();
Path mRoundedRectClipPath = new Path();
+ Rect mTmpRect = new Rect();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
TaskViewCallbacks mCb;
+ // Optimizations
+ ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateDimOverlayFromScale();
+ }
+ };
+
public TaskView(Context context) {
this(context, null);
@@ -79,13 +92,14 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
setWillNotDraw(false);
+ setDim(getDim());
}
@Override
protected void onFinishInflate() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mMaxDim = config.taskStackMaxDim;
+ mMaxDim = mConfig.taskStackMaxDim;
// By default, all views are clipped to other views in their stack
mClipViewInStack = true;
@@ -104,8 +118,7 @@
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Update the rounded rect clip path
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- float radius = config.taskViewRoundedCornerRadiusPx;
+ float radius = mConfig.taskViewRoundedCornerRadiusPx;
mRoundedRectClipPath.reset();
mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
radius, radius, Path.Direction.CW);
@@ -113,7 +126,7 @@
// Update the outline
Outline o = new Outline();
o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
- config.taskViewShadowOutlineBottomInsetPx, radius);
+ mConfig.taskViewShadowOutlineBottomInsetPx, radius);
setOutline(o);
}
@@ -141,9 +154,10 @@
/** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
TaskViewTransform toTransform, int duration) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- int minZ = config.taskViewTranslationZMinPx;
- int incZ = config.taskViewTranslationZIncrementPx;
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
+ "duration: " + duration, Console.AnsiPurple);
+ }
// Update the bar view
mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration);
@@ -153,33 +167,28 @@
if (animateFromTransform != null) {
setTranslationY(animateFromTransform.translationY);
if (Constants.DebugFlags.App.EnableShadows) {
- setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ)));
+ setTranslationZ(animateFromTransform.translationZ);
}
setScaleX(animateFromTransform.scale);
setScaleY(animateFromTransform.scale);
setAlpha(animateFromTransform.alpha);
}
if (Constants.DebugFlags.App.EnableShadows) {
- animate().translationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
+ animate().translationZ(toTransform.translationZ);
}
animate().translationY(toTransform.translationY)
.scaleX(toTransform.scale)
.scaleY(toTransform.scale)
.alpha(toTransform.alpha)
+ .setStartDelay(0)
.setDuration(duration)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .withLayer()
- .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateDimOverlayFromScale();
- }
- })
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setUpdateListener(mUpdateDimListener)
.start();
} else {
setTranslationY(toTransform.translationY);
if (Constants.DebugFlags.App.EnableShadows) {
- setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ)));
+ setTranslationZ(toTransform.translationZ);
}
setScaleX(toTransform.scale);
setScaleY(toTransform.scale);
@@ -199,6 +208,7 @@
setScaleX(1f);
setScaleY(1f);
setAlpha(1f);
+ setDim(0);
invalidate();
}
@@ -223,40 +233,129 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateOnEnterRecents() {
- mBarView.setVisibility(View.INVISIBLE);
+ public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
+ Rect taskRect) {
+ if (mConfig.launchedFromAppWithScreenshot) {
+ if (isTaskViewFrontMost) {
+ // Hide the task view as we are going to animate the full screenshot into view
+ // and then replace it with this view once we are done
+ setVisibility(View.INVISIBLE);
+ // Also hide the front most task bar view so we can animate it in
+ mBarView.prepareAnimateEnterRecents();
+ } else {
+ // Top align the task views
+ setTranslationY(offsetY);
+ setScaleX(1f);
+ setScaleY(1f);
+ }
+
+ } else if (mConfig.launchedFromAppWithThumbnail) {
+ if (isTaskViewFrontMost) {
+ // Hide the front most task bar view so we can animate it in
+ mBarView.prepareAnimateEnterRecents();
+ // Set the dim to 0 so we can animate it in
+ setDim(0);
+ }
+
+ } else if (mConfig.launchedFromHome) {
+ // Move the task view off screen (below) so we can animate it in
+ setTranslationY(offscreenY);
+ setScaleX(1f);
+ setScaleY(1f);
+ }
}
/** Animates this task view as it enters recents */
- public void animateOnEnterRecents() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mBarView.setVisibility(View.VISIBLE);
- mBarView.setTranslationY(-mBarView.getMeasuredHeight());
- mBarView.animate()
- .translationY(0)
- .setStartDelay(200)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .setDuration(config.taskBarEnterAnimDuration)
+ public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) {
+ TaskViewTransform transform = ctx.transform;
+
+ if (mConfig.launchedFromAppWithScreenshot) {
+ if (ctx.isFrontMost) {
+ // Animate the full screenshot down first, before swapping with this task view
+ ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
+ @Override
+ public void run() {
+ // Animate the task bar of the first task view
+ mBarView.animateOnEnterRecents(0);
+ setVisibility(View.VISIBLE);
+ }
+ });
+ } else {
+ // Animate the tasks down behind the full screenshot
+ animate()
+ .scaleX(transform.scale)
+ .scaleY(transform.scale)
+ .translationY(transform.translationY)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.linearOutSlowInInterpolator)
+ .setDuration(475)
+ .withLayer()
+ .start();
+ }
+
+ } else if (mConfig.launchedFromAppWithThumbnail) {
+ if (ctx.isFrontMost) {
+ // Animate the task bar of the first task view
+ mBarView.animateOnEnterRecents(mConfig.taskBarEnterAnimDelay);
+ // Animate the dim into view as well
+ ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", getDimOverlayFromScale());
+ anim.setStartDelay(mConfig.taskBarEnterAnimDelay);
+ anim.setDuration(mConfig.taskBarEnterAnimDuration);
+ anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+ anim.start();
+ }
+
+ } else if (mConfig.launchedFromHome) {
+ // Animate the tasks up
+ int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
+ int delay = mConfig.taskBarEnterAnimDelay +
+ frontIndex * mConfig.taskViewEnterFromHomeDelay;
+ animate()
+ .scaleX(transform.scale)
+ .scaleY(transform.scale)
+ .translationY(transform.translationY)
+ .setStartDelay(delay)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
+ .withLayer()
+ .start();
+ }
+ }
+
+ /** Animates this task view as it leaves recents */
+ public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) {
+ animate()
+ .translationY(ctx.offscreenTranslationY)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
.withLayer()
+ .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
+ ctx.postAnimationTrigger.increment();
+ }
+
+ /** Animates this task view if the user does not interact with the stack after a certain time. */
+ public void animateOnNoUserInteraction() {
+ mBarView.animateOnNoUserInteraction();
+ }
+
+ /** Mark this task view that the user does has not interacted with the stack after a certain time. */
+ public void setOnNoUserInteraction() {
+ mBarView.setOnNoUserInteraction();
}
/** Animates this task view as it exits recents */
- public void animateOnLeavingRecents(final Runnable r) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mBarView.animate()
- .translationY(-mBarView.getMeasuredHeight())
- .setStartDelay(0)
- .setInterpolator(config.fastOutLinearInInterpolator)
- .setDuration(config.taskBarExitAnimDuration)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- post(r);
- }
- })
- .start();
+ public void animateOnLaunchingTask(final Runnable r) {
+ mBarView.animateOnLaunchingTask(r);
+
+ // Animate the dim
+ if (mDim > 0) {
+ ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0);
+ anim.setDuration(mConfig.taskBarExitAnimDuration);
+ anim.setInterpolator(mConfig.fastOutLinearInInterpolator);
+ anim.start();
+ }
}
/** Animates the deletion of this task view */
@@ -264,20 +363,24 @@
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- animate().translationX(config.taskViewRemoveAnimTranslationXPx)
+ animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .setDuration(config.taskViewRemoveAnimDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewRemoveAnimDuration)
.withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
- post(r);
+ // We just throw this into a runnable because starting a view property
+ // animation using layers can cause inconsisten results if we try and
+ // update the layers while the animation is running. In some cases,
+ // the runnabled passed in may start an animation which also uses layers
+ // so we defer all this by posting this.
+ r.run();
// Re-enable clipping with the stack (we will reuse this view)
- setClipViewInStack(false);
+ setClipViewInStack(true);
}
})
.start();
@@ -295,11 +398,13 @@
/** Enable the hw layers on this task view */
void enableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mBarView.enableHwLayers();
}
/** Disable the hw layers on this task view */
void disableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mBarView.disableHwLayers();
}
/**
@@ -307,7 +412,7 @@
* view.
*/
boolean shouldClipViewInStack() {
- return mClipViewInStack;
+ return mClipViewInStack && (getVisibility() == View.VISIBLE);
}
/** Sets whether this view should be clipped, or clipped against. */
@@ -315,20 +420,35 @@
if (clip != mClipViewInStack) {
mClipViewInStack = clip;
if (getParent() instanceof View) {
- Rect r = new Rect();
- getHitRect(r);
- ((View) getParent()).invalidate(r);
+ getHitRect(mTmpRect);
+ ((View) getParent()).invalidate(mTmpRect);
}
}
}
- /** Update the dim as a function of the scale of this view. */
- void updateDimOverlayFromScale() {
+ /** Returns the current dim. */
+ public void setDim(int dim) {
+ mDim = dim;
+ postInvalidateOnAnimation();
+ }
+
+ /** Returns the current dim. */
+ public int getDim() {
+ return mDim;
+ }
+
+ /** Compute the dim as a function of the scale of this view. */
+ int getDimOverlayFromScale() {
float minScale = Constants.Values.TaskStackView.StackPeekMinScale;
float scaleRange = 1f - minScale;
float dim = (1f - getScaleX()) / scaleRange;
dim = mDimInterpolator.getInterpolation(Math.min(dim, 1f));
- mDim = Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
+ return Math.max(0, Math.min(mMaxDim, (int) (dim * 255)));
+ }
+
+ /** Update the dim as a function of the scale of this view. */
+ void updateDimOverlayFromScale() {
+ setDim(getDimOverlayFromScale());
}
@Override
@@ -393,8 +513,7 @@
mBarView.mApplicationIcon.setOnClickListener(this);
mBarView.mDismissButton.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.developerOptionsEnabled) {
+ if (mConfig.developerOptionsEnabled) {
mBarView.mApplicationIcon.setOnLongClickListener(this);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index e6391a8..4a76872 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -22,6 +22,7 @@
/* The transform state for a task view */
public class TaskViewTransform {
public int translationY = 0;
+ public int translationZ = 0;
public float scale = 1f;
public float alpha = 1f;
public float dismissAlpha = 1f;
@@ -35,6 +36,7 @@
public TaskViewTransform(TaskViewTransform o) {
translationY = o.translationY;
+ translationZ = o.translationZ;
scale = o.scale;
alpha = o.alpha;
dismissAlpha = o.dismissAlpha;
@@ -45,7 +47,8 @@
@Override
public String toString() {
- return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha +
- " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha;
+ return "TaskViewTransform y: " + translationY + " z: " + translationZ + " scale: " + scale +
+ " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
+ " dismissAlpha: " + dismissAlpha;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
new file mode 100644
index 0000000..b5e8ffd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.graphics.Rect;
+import com.android.systemui.recents.ReferenceCountedTrigger;
+
+/* Common code related to view animations */
+public class ViewAnimation {
+
+ /* The animation context for a task view animation into Recents */
+ public static class TaskViewEnterContext {
+ // The full screenshot view that we are animating down
+ FullScreenTransitionView fullScreenshot;
+ // The transform of the current task view
+ TaskViewTransform transform;
+ // The stack rect that the transform is relative to
+ Rect stackRectSansPeek;
+ // The task rect
+ Rect taskRect;
+ // The view index of the current task view
+ int stackViewIndex;
+ // The total number of task views
+ int stackViewCount;
+ // Whether this is the front most task view
+ boolean isFrontMost;
+
+ public TaskViewEnterContext(FullScreenTransitionView fss) {
+ fullScreenshot = fss;
+ }
+ }
+
+ /* The animation context for a task view animation out of Recents */
+ public static class TaskViewExitContext {
+ // A trigger to run some logic when all the animations complete. This works around the fact
+ // that it is difficult to coordinate ViewPropertyAnimators
+ ReferenceCountedTrigger postAnimationTrigger;
+ // The translationY to apply to a TaskView to move it off screen
+ int offscreenTranslationY;
+
+ public TaskViewExitContext(ReferenceCountedTrigger t) {
+ postAnimationTrigger = t;
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index a770f58..80e85f9 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -34,6 +34,7 @@
public class BrightnessController implements ToggleSlider.Listener {
private static final String TAG = "StatusBar.BrightnessController";
+ private static final boolean SHOW_AUTOMATIC_ICON = false;
/**
* {@link android.provider.Settings.System#SCREEN_AUTO_BRIGHTNESS_ADJ} uses the range [-1, 1].
@@ -232,7 +233,7 @@
private void updateIcon(boolean automatic) {
if (mIcon != null) {
- mIcon.setImageResource(automatic ?
+ mIcon.setImageResource(automatic && SHOW_AUTOMATIC_ICON ?
com.android.systemui.R.drawable.ic_qs_brightness_auto_on :
com.android.systemui.R.drawable.ic_qs_brightness_auto_off);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
index d67e7cb..a3b10f2 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
@@ -21,8 +21,16 @@
import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -43,15 +51,18 @@
/**
* A quick and dirty view to show a user switcher.
*/
-public class UserSwitcherHostView extends FrameLayout implements ListView.OnItemClickListener {
+public class UserSwitcherHostView extends FrameLayout
+ implements ListView.OnItemClickListener, View.OnClickListener {
private static final String TAG = "UserSwitcherDialog";
private ArrayList<UserInfo> mUserInfo = new ArrayList<UserInfo>();
+ private UserInfo mGuestUser;
private Adapter mAdapter = new Adapter();
private UserManager mUserManager;
private Runnable mFinishRunnable;
private ListView mListView;
+ private boolean mGuestUserEnabled;
public UserSwitcherHostView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -60,6 +71,9 @@
return;
}
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+
+ mGuestUserEnabled = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.GUEST_USER_ENABLED, 0) == 1;
}
public UserSwitcherHostView(Context context, AttributeSet attrs) {
@@ -80,7 +94,39 @@
@Override
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
- int userId = mAdapter.getItem(position).id;
+ // Last item is the guest
+ if (position == mUserInfo.size()) {
+ postDelayed(new Runnable() {
+ public void run() {
+ switchToGuestUser();
+ }
+ }, 100);
+ } else {
+ final int userId = mAdapter.getItem(position).id;
+ postDelayed(new Runnable() {
+ public void run() {
+ switchUser(userId);
+ }
+ }, 100);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ // Delete was clicked
+ postDelayed(new Runnable() {
+ public void run() {
+ if (mGuestUser != null) {
+ switchUser(0);
+ mUserManager.removeUser(mGuestUser.id);
+ mGuestUser = null;
+ refreshUsers();
+ }
+ }
+ }, 100);
+ }
+
+ private void switchUser(int userId) {
try {
WindowManagerGlobal.getWindowManagerService().lockNow(null);
ActivityManagerNative.getDefault().switchUser(userId);
@@ -90,6 +136,15 @@
}
}
+ private void switchToGuestUser() {
+ if (mGuestUser == null) {
+ // No guest user. Create one.
+ mGuestUser = mUserManager.createGuest(mContext,
+ mContext.getResources().getString(R.string.guest_nickname));
+ }
+ switchUser(mGuestUser.id);
+ }
+
private void finish() {
if (mFinishRunnable != null) {
mFinishRunnable.run();
@@ -119,9 +174,12 @@
public void refreshUsers() {
mUserInfo.clear();
+ mGuestUser = null;
List<UserInfo> users = mUserManager.getUsers(true);
for (UserInfo user : users) {
- if (!user.isManagedProfile()) {
+ if (user.isGuest()) {
+ mGuestUser = user;
+ } else if (!user.isManagedProfile()) {
mUserInfo.add(user);
}
}
@@ -132,17 +190,25 @@
@Override
public int getCount() {
- return mUserInfo.size();
+ return mUserInfo.size() + (mGuestUserEnabled ? 1 : 0);
}
@Override
public UserInfo getItem(int position) {
- return mUserInfo.get(position);
+ if (position < mUserInfo.size()) {
+ return mUserInfo.get(position);
+ } else {
+ return mGuestUser;
+ }
}
@Override
public long getItemId(int position) {
- return getItem(position).serialNumber;
+ if (position < mUserInfo.size()) {
+ return getItem(position).serialNumber;
+ } else {
+ return mGuestUser != null ? mGuestUser.serialNumber : -1;
+ }
}
@Override
@@ -161,18 +227,46 @@
ViewHolder h = new ViewHolder();
h.name = (TextView) v.findViewById(R.id.user_name);
h.picture = (ImageView) v.findViewById(R.id.user_picture);
+ h.delete = (ImageView) v.findViewById(R.id.user_delete);
v.setTag(h);
return v;
}
private void bindView(ViewHolder h, UserInfo item) {
- h.name.setText(item.name);
- h.picture.setImageBitmap(mUserManager.getUserIcon(item.id));
+ if (item != null) {
+ h.name.setText(item.name);
+ h.picture.setImageBitmap(circularClip(mUserManager.getUserIcon(item.id)));
+ h.delete.setVisibility(item.isGuest() ? View.VISIBLE : View.GONE);
+ h.delete.setOnClickListener(UserSwitcherHostView.this);
+ if (item.isGuest()) {
+ h.picture.setImageResource(R.drawable.ic_account_circle);
+ }
+ } else {
+ h.name.setText(R.string.guest_new_guest);
+ h.picture.setImageResource(R.drawable.ic_account_circle);
+ h.delete.setVisibility(View.GONE);
+ }
+ }
+
+ private Bitmap circularClip(Bitmap input) {
+ if (input == null) {
+ return null;
+ }
+ Bitmap output = Bitmap.createBitmap(input.getWidth(),
+ input.getHeight(), Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+ final Paint paint = new Paint();
+ paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+ paint.setAntiAlias(true);
+ canvas.drawCircle(input.getWidth() / 2, input.getHeight() / 2, input.getWidth() / 2,
+ paint);
+ return output;
}
class ViewHolder {
TextView name;
ImageView picture;
+ ImageView delete;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index dcd187c..8d19f50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -33,11 +33,13 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewAnimationUtils;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.PathInterpolator;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -88,8 +90,8 @@
private boolean mDimmed;
- private int mBgResId = com.android.internal.R.drawable.notification_quantum_bg;
- private int mDimmedBgResId = com.android.internal.R.drawable.notification_quantum_bg_dim;
+ private int mBgResId = com.android.internal.R.drawable.notification_material_bg;
+ private int mDimmedBgResId = com.android.internal.R.drawable.notification_material_bg_dim;
private int mBgTint = 0;
private int mDimmedBgTint = 0;
@@ -141,7 +143,7 @@
setClipToPadding(false);
mAppearAnimationFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
mRoundedRectCornerRadius = getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
+ com.android.internal.R.dimen.notification_material_rounded_rect_radius);
}
@Override
@@ -156,7 +158,7 @@
private final Runnable mTapTimeoutRunnable = new Runnable() {
@Override
public void run() {
- makeInactive();
+ makeInactive(true /* animate */);
}
};
@@ -181,7 +183,7 @@
break;
case MotionEvent.ACTION_MOVE:
if (!isWithinTouchSlop(event)) {
- makeInactive();
+ makeInactive(true /* animate */);
return false;
}
break;
@@ -191,14 +193,17 @@
makeActive();
postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
} else {
- performClick();
+ boolean performed = performClick();
+ if (performed) {
+ removeCallbacks(mTapTimeoutRunnable);
+ }
}
} else {
- makeInactive();
+ makeInactive(true /* animate */);
}
break;
case MotionEvent.ACTION_CANCEL:
- makeInactive();
+ makeInactive(true /* animate */);
break;
default:
break;
@@ -219,7 +224,8 @@
int heightHalf = mBackgroundNormal.getActualHeight()/2;
float radius = (float) Math.sqrt(widthHalf*widthHalf + heightHalf*heightHalf);
ValueAnimator animator =
- mBackgroundNormal.createRevealAnimator(widthHalf, heightHalf, 0, radius);
+ ViewAnimationUtils.createCircularReveal(mBackgroundNormal,
+ widthHalf, heightHalf, 0, radius);
mBackgroundNormal.setVisibility(View.VISIBLE);
Interpolator interpolator;
Interpolator alphaInterpolator;
@@ -254,10 +260,14 @@
/**
* Cancels the hotspot and makes the notification inactive.
*/
- private void makeInactive() {
+ public void makeInactive(boolean animate) {
if (mActivated) {
if (mDimmed) {
- startActivateAnimation(true /* reverse */);
+ if (animate) {
+ startActivateAnimation(true /* reverse */);
+ } else {
+ mBackgroundNormal.setVisibility(View.INVISIBLE);
+ }
}
mActivated = false;
}
@@ -348,6 +358,7 @@
mBackgroundDimmed.setVisibility(View.INVISIBLE);
mBackgroundNormal.setVisibility(View.VISIBLE);
mBackgroundNormal.setAlpha(1f);
+ removeCallbacks(mTapTimeoutRunnable);
}
}
@@ -578,7 +589,7 @@
}
public interface OnActivatedListener {
- void onActivated(View view);
- void onActivationReset(View view);
+ void onActivated(ActivatableNotificationView view);
+ void onActivationReset(ActivatableNotificationView view);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedView.java
new file mode 100644
index 0000000..d2fe858
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaOptimizedView.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 com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * A View which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class AlphaOptimizedView extends View
+{
+ public AlphaOptimizedView(Context context) {
+ super(context);
+ }
+
+ public AlphaOptimizedView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AlphaOptimizedView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AlphaOptimizedView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 06cc476..20684a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -48,7 +48,7 @@
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
@@ -80,17 +80,17 @@
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
+import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.Locale;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
public abstract class BaseStatusBar extends SystemUI implements
- CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener {
+ CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
+ RecentsComponent.Callbacks {
public static final String TAG = "StatusBar";
public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
@@ -106,6 +106,7 @@
protected static final int MSG_SHOW_HEADS_UP = 1026;
protected static final int MSG_HIDE_HEADS_UP = 1027;
protected static final int MSG_ESCALATE_HEADS_UP = 1028;
+ protected static final int MSG_DECAY_HEADS_UP = 1029;
protected static final boolean ENABLE_HEADS_UP = true;
// scores above this threshold should be displayed in heads up mode.
@@ -129,8 +130,9 @@
protected NotificationData mNotificationData = new NotificationData();
protected NotificationStackScrollLayout mStackScroller;
- protected NotificationData.Entry mInterruptingNotificationEntry;
- protected long mInterruptingNotificationTime;
+ // for heads up notifications
+ protected HeadsUpNotificationView mHeadsUpNotificationView;
+ protected int mHeadsUpNotificationDecay;
// used to notify status bar for suppressing notification LED
protected boolean mPanelSlightlyVisible;
@@ -291,7 +293,7 @@
public void onListenerConnected() {
if (DEBUG) Log.d(TAG, "onListenerConnected");
final StatusBarNotification[] notifications = getActiveNotifications();
- final Ranking currentRanking = getCurrentRanking();
+ final RankingMap currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -303,41 +305,41 @@
}
@Override
- public void onNotificationPosted(final StatusBarNotification sbn) {
+ public void onNotificationPosted(final StatusBarNotification sbn,
+ final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
- final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
- if (mNotificationData.findByKey(sbn.getKey()) != null) {
- updateNotificationInternal(sbn, currentRanking);
+ if (mNotificationData.findByKey(sbn.getKey()) != null ||
+ isHeadsUp(sbn.getKey())) {
+ updateNotificationInternal(sbn, rankingMap);
} else {
- addNotificationInternal(sbn, currentRanking);
+ addNotificationInternal(sbn, rankingMap);
}
}
});
}
@Override
- public void onNotificationRemoved(final StatusBarNotification sbn) {
+ public void onNotificationRemoved(final StatusBarNotification sbn,
+ final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
- final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
- removeNotificationInternal(sbn.getKey(), currentRanking);
+ removeNotificationInternal(sbn.getKey(), rankingMap);
}
});
}
@Override
- public void onNotificationRankingUpdate() {
+ public void onNotificationRankingUpdate(final RankingMap rankingMap) {
if (DEBUG) Log.d(TAG, "onRankingUpdate");
- final Ranking currentRanking = getCurrentRanking();
mHandler.post(new Runnable() {
@Override
public void run() {
- updateRankingInternal(currentRanking);
+ updateRankingInternal(rankingMap);
}
});
}
@@ -384,6 +386,7 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mRecents = getComponent(RecentsComponent.class);
+ mRecents.setCallback(this);
mLocale = mContext.getResources().getConfiguration().locale;
mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
@@ -466,6 +469,10 @@
// should be overridden
}
+ public boolean isHeadsUp(String key) {
+ return mHeadsUpNotificationView != null && mHeadsUpNotificationView.isShowing(key);
+ }
+
public boolean notificationIsForCurrentProfiles(StatusBarNotification n) {
final int thisUserId = mCurrentUserId;
final int notificationUserId = n.getUserId();
@@ -505,8 +512,8 @@
protected View updateNotificationVetoButton(View row, StatusBarNotification n) {
View vetoButton = row.findViewById(R.id.veto);
- if (n.isClearable() || (mInterruptingNotificationEntry != null
- && mInterruptingNotificationEntry.row == row)) {
+ if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null
+ && mHeadsUpNotificationView.getEntry().row == row)) {
final String _pkg = n.getPackageName();
final String _tag = n.getTag();
final int _id = n.getId();
@@ -545,21 +552,20 @@
if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
// Using custom RemoteViews
- if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
- entry.row.setBackgroundResource(R.drawable.notification_row_legacy_bg);
- } else if (version < Build.VERSION_CODES.L) {
+ if (version >= Build.VERSION_CODES.GINGERBREAD && version < Build.VERSION_CODES.L) {
entry.row.setBackgroundResourceIds(
com.android.internal.R.drawable.notification_bg,
com.android.internal.R.drawable.notification_bg_dim);
+ entry.legacy = true;
}
} else {
// Using platform templates
final int color = sbn.getNotification().color;
if (isMediaNotification(entry)) {
entry.row.setBackgroundResourceIds(
- com.android.internal.R.drawable.notification_quantum_bg,
+ com.android.internal.R.drawable.notification_material_bg,
color,
- com.android.internal.R.drawable.notification_quantum_bg_dim,
+ com.android.internal.R.drawable.notification_material_bg_dim,
color);
}
}
@@ -764,8 +770,19 @@
}
}
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ // Do nothing
+ }
+
public abstract void resetHeadsUpDecayTimer();
+ public abstract void scheduleHeadsUpOpen();
+
+ public abstract void scheduleHeadsUpClose();
+
+ public abstract void scheduleHeadsUpEscalation();
+
/**
* Save the current "public" (locked and secure) state of the lockscreen.
*/
@@ -797,6 +814,18 @@
return mUsersAllowingPrivateNotifications.get(userHandle);
}
+ public void onNotificationClear(StatusBarNotification notification) {
+ try {
+ mBarService.onNotificationClear(
+ notification.getPackageName(),
+ notification.getTag(),
+ notification.getId(),
+ notification.getUserId());
+ } catch (android.os.RemoteException ex) {
+ // oh well
+ }
+ }
+
protected class H extends Handler {
public void handleMessage(Message m) {
Intent intent;
@@ -874,10 +903,7 @@
}
public boolean inflateViews(NotificationData.Entry entry, ViewGroup parent, boolean isHeadsUp) {
- int minHeight =
- mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height);
- int maxHeight =
- mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
+ int maxHeight = mRowMaxHeight;
StatusBarNotification sbn = entry.notification;
RemoteViews contentView = sbn.getNotification().contentView;
RemoteViews bigContentView = sbn.getNotification().bigContentView;
@@ -981,7 +1007,7 @@
if (publicViewLocal == null) {
// Add a basic notification template
publicViewLocal = LayoutInflater.from(mContext).inflate(
- com.android.internal.R.layout.notification_template_quantum_base,
+ com.android.internal.R.layout.notification_template_material_base,
expandedPublic, true);
final TextView title = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.title);
@@ -992,7 +1018,10 @@
title.setText(entry.notification.getPackageName());
}
- final ImageView icon = (ImageView) publicViewLocal.findViewById(com.android.internal.R.id.icon);
+ final ImageView icon = (ImageView) publicViewLocal.findViewById(
+ com.android.internal.R.id.icon);
+ final ImageView profileIcon = (ImageView) publicViewLocal.findViewById(
+ com.android.internal.R.id.profile_icon);
final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
entry.notification.getUser(),
@@ -1008,9 +1037,22 @@
com.android.internal.R.drawable.notification_icon_legacy_bg_inset);
}
- final TextView text = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.text);
+ if (profileIcon != null) {
+ Drawable profileDrawable
+ = mUserManager.getBadgeForUser(entry.notification.getUser());
+ if (profileDrawable != null) {
+ profileIcon.setImageDrawable(profileDrawable);
+ profileIcon.setVisibility(View.VISIBLE);
+ } else {
+ profileIcon.setVisibility(View.GONE);
+ }
+ }
+
+ final TextView text = (TextView) publicViewLocal.findViewById(
+ com.android.internal.R.id.text);
text.setText("Unlock your device to see this notification.");
+ entry.autoRedacted = true;
// TODO: fill out "time" as well
}
@@ -1024,7 +1066,7 @@
}
}
entry.row = row;
- entry.row.setHeightRange(mRowMinHeight, mRowMaxHeight);
+ entry.row.setHeightRange(mRowMinHeight, maxHeight);
entry.row.setOnActivatedListener(this);
entry.row.setIsBelowSpeedBump(isBelowSpeedBump(entry.notification));
entry.expanded = contentViewLocal;
@@ -1086,7 +1128,7 @@
try {
if (mIsHeadsUp) {
- mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+ mHeadsUpNotificationView.clear();
}
mBarService.onNotificationClick(mNotificationKey);
} catch (RemoteException ex) {
@@ -1142,7 +1184,7 @@
}
}
- protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) {
+ protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
NotificationData.Entry entry = mNotificationData.remove(key, ranking);
if (entry == null) {
Log.w(TAG, "removeNotification for unknown key: " + key);
@@ -1182,7 +1224,7 @@
return entry;
}
- protected void addNotificationViews(Entry entry, Ranking ranking) {
+ protected void addNotificationViews(Entry entry, RankingMap ranking) {
if (entry == null) {
return;
}
@@ -1191,7 +1233,7 @@
updateNotifications();
}
- private void addNotificationViews(StatusBarNotification notification, Ranking ranking) {
+ private void addNotificationViews(StatusBarNotification notification, RankingMap ranking) {
addNotificationViews(createNotificationViews(notification), ranking);
}
@@ -1277,9 +1319,9 @@
}
public abstract void addNotificationInternal(StatusBarNotification notification,
- Ranking ranking);
+ RankingMap ranking);
- protected abstract void updateRankingInternal(Ranking ranking);
+ protected abstract void updateRankingInternal(RankingMap ranking);
@Override
public void removeNotification(String key) {
@@ -1288,7 +1330,7 @@
}
}
- public abstract void removeNotificationInternal(String key, Ranking ranking);
+ public abstract void removeNotificationInternal(String key, RankingMap ranking);
public void updateNotification(StatusBarNotification notification) {
if (!USE_NOTIFICATION_LISTENER) {
@@ -1296,12 +1338,18 @@
}
}
- public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+ public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
- final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
+ final String key = notification.getKey();
+ boolean wasHeadsUp = isHeadsUp(key);
+ NotificationData.Entry oldEntry;
+ if (wasHeadsUp) {
+ oldEntry = mHeadsUpNotificationView.getEntry();
+ } else {
+ oldEntry = mNotificationData.findByKey(key);
+ }
if (oldEntry == null) {
- Log.w(TAG, "updateNotification for unknown key: " + notification.getKey());
return;
}
@@ -1367,61 +1415,99 @@
&& oldPublicContentView.getPackage() != null
&& oldPublicContentView.getPackage().equals(publicContentView.getPackage())
&& oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
-
-
boolean updateTicker = notification.getNotification().tickerText != null
&& !TextUtils.equals(notification.getNotification().tickerText,
oldEntry.notification.getNotification().tickerText);
+
+ final boolean shouldInterrupt = shouldInterrupt(notification);
+ final boolean alertAgain = alertAgain(oldEntry);
+ boolean updateSuccessful = false;
if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
&& publicUnchanged) {
- if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey());
+ if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
oldEntry.notification = notification;
try {
- updateNotificationViews(oldEntry, notification);
-
- if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
- && oldNotification == mInterruptingNotificationEntry.notification) {
- if (!shouldInterrupt(notification)) {
- if (DEBUG) Log.d(TAG, "no longer interrupts!");
- mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
- } else {
- if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification);
- mInterruptingNotificationEntry.notification = notification;
- updateHeadsUpViews(mInterruptingNotificationEntry, notification);
+ if (oldEntry.icon != null) {
+ // Update the icon
+ final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
+ notification.getUser(),
+ notification.getNotification().icon,
+ notification.getNotification().iconLevel,
+ notification.getNotification().number,
+ notification.getNotification().tickerText);
+ if (!oldEntry.icon.set(ic)) {
+ handleNotificationError(notification, "Couldn't update icon: " + ic);
+ return;
}
}
- // Update the icon.
- final StatusBarIcon ic = new StatusBarIcon(notification.getPackageName(),
- notification.getUser(),
- notification.getNotification().icon, notification.getNotification().iconLevel,
- notification.getNotification().number,
- notification.getNotification().tickerText);
- if (!oldEntry.icon.set(ic)) {
- handleNotificationError(notification, "Couldn't update icon: " + ic);
- return;
+ if (wasHeadsUp) {
+ if (shouldInterrupt) {
+ updateHeadsUpViews(oldEntry, notification);
+ if (alertAgain) {
+ resetHeadsUpDecayTimer();
+ }
+ } else {
+ // we updated the notification above, so release to build a new shade entry
+ mHeadsUpNotificationView.releaseAndClose();
+ return;
+ }
+ } else {
+ if (shouldInterrupt && alertAgain) {
+ removeNotificationViews(key, ranking);
+ addNotificationInternal(notification, ranking); //this will pop the headsup
+ } else {
+ updateNotificationViews(oldEntry, notification);
+ }
}
mNotificationData.updateRanking(ranking);
updateNotifications();
+ updateSuccessful = true;
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
- removeNotificationViews(notification.getKey(), ranking);
- addNotificationViews(notification, ranking);
}
- } else {
- if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey());
- if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
- removeNotificationViews(notification.getKey(), ranking);
- addNotificationViews(notification, ranking); // will also replace the heads up
- final NotificationData.Entry newEntry = mNotificationData.findByKey(
- notification.getKey());
- final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
- if (userChangedExpansion) {
- boolean userExpanded = oldEntry.row.isUserExpanded();
- newEntry.row.setUserExpanded(userExpanded);
- newEntry.row.notifyHeightChanged();
+ }
+ if (!updateSuccessful) {
+ if (DEBUG) Log.d(TAG, "not reusing notification for key: " + key);
+ if (wasHeadsUp) {
+ if (shouldInterrupt) {
+ if (DEBUG) Log.d(TAG, "rebuilding heads up for key: " + key);
+ Entry newEntry = new Entry(notification, null);
+ ViewGroup holder = mHeadsUpNotificationView.getHolder();
+ if (inflateViewsForHeadsUp(newEntry, holder)) {
+ mHeadsUpNotificationView.showNotification(newEntry);
+ if (alertAgain) {
+ resetHeadsUpDecayTimer();
+ }
+ } else {
+ Log.w(TAG, "Couldn't create new updated headsup for package "
+ + contentView.getPackage());
+ }
+ } else {
+ if (DEBUG) Log.d(TAG, "releasing heads up for key: " + key);
+ oldEntry.notification = notification;
+ mHeadsUpNotificationView.releaseAndClose();
+ return;
+ }
+ } else {
+ if (shouldInterrupt && alertAgain) {
+ if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key);
+ removeNotificationViews(key, ranking);
+ addNotificationInternal(notification, ranking); //this will pop the headsup
+ } else {
+ if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key);
+ removeNotificationViews(key, ranking);
+ addNotificationViews(notification, ranking);
+ final NotificationData.Entry newEntry = mNotificationData.findByKey(key);
+ final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
+ if (userChangedExpansion) {
+ boolean userExpanded = oldEntry.row.isUserExpanded();
+ newEntry.row.setUserExpanded(userExpanded);
+ newEntry.row.notifyHeightChanged();
+ }
+ }
}
}
@@ -1496,11 +1582,17 @@
}
protected void notifyHeadsUpScreenOn(boolean screenOn) {
- if (!screenOn && mInterruptingNotificationEntry != null) {
- mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
+ if (!screenOn) {
+ scheduleHeadsUpEscalation();
}
}
+ private boolean alertAgain(Entry entry) {
+ final StatusBarNotification sbn = entry.notification;
+ return entry == null || !entry.hasInterrupted()
+ || (sbn.getNotification().flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
+ }
+
protected boolean shouldInterrupt(StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
// some predicates to make the boolean logic legible
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 5b2ea0b..517a4e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -25,7 +25,6 @@
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-
import com.android.systemui.ExpandHelper;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
@@ -87,6 +86,7 @@
captureStartingChild(mInitialTouchX, mInitialTouchY);
mInitialTouchY = y;
mInitialTouchX = x;
+ mOnDragDownListener.onTouchSlopExceeded();
return true;
}
break;
@@ -202,5 +202,6 @@
void onDraggedDown(View startingChild);
void onDragDownReset();
void onThresholdReached();
+ void onTouchSlopExceeded();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 84005d1..5bad602 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -209,7 +209,7 @@
}
public int getMaxExpandHeight() {
- return mMaxExpandHeight;
+ return mShowingPublic ? mRowMinHeight : mMaxExpandHeight;
}
/**
@@ -221,30 +221,35 @@
@Override
public boolean isContentExpandable() {
- return mPrivateLayout.isContentExpandable();
+ NotificationContentView showingLayout = getShowingLayout();
+ return showingLayout.isContentExpandable();
}
@Override
public void setActualHeight(int height, boolean notifyListeners) {
mPrivateLayout.setActualHeight(height);
+ mPublicLayout.setActualHeight(height);
invalidate();
super.setActualHeight(height, notifyListeners);
}
@Override
public int getMaxHeight() {
- return mPrivateLayout.getMaxHeight();
+ NotificationContentView showingLayout = getShowingLayout();
+ return showingLayout.getMaxHeight();
}
@Override
public int getMinHeight() {
- return mPrivateLayout.getMinHeight();
+ NotificationContentView showingLayout = getShowingLayout();
+ return showingLayout.getMinHeight();
}
@Override
public void setClipTopAmount(int clipTopAmount) {
super.setClipTopAmount(clipTopAmount);
mPrivateLayout.setClipTopAmount(clipTopAmount);
+ mPublicLayout.setClipTopAmount(clipTopAmount);
}
public boolean isBelowSpeedBump() {
@@ -256,6 +261,16 @@
}
public void notifyContentUpdated() {
+ mPublicLayout.notifyContentUpdated();
mPrivateLayout.notifyContentUpdated();
}
+
+ public boolean isShowingLayoutLayouted() {
+ NotificationContentView showingLayout = getShowingLayout();
+ return showingLayout.getWidth() != 0;
+ }
+
+ private NotificationContentView getShowingLayout() {
+ return mShowingPublic ? mPublicLayout : mPrivateLayout;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 088f076..ac2537c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -69,7 +69,9 @@
? MeasureSpec.makeMeasureSpec(ownMaxHeight, MeasureSpec.EXACTLY)
: MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
}
- child.measure(widthMeasureSpec, childHeightSpec);
+ child.measure(
+ getChildMeasureSpec(widthMeasureSpec, 0 /* padding */, layoutParams.width),
+ childHeightSpec);
int childHeight = child.getMeasuredHeight();
maxChildHeight = Math.max(maxChildHeight, childHeight);
} else {
@@ -79,7 +81,9 @@
int ownHeight = hasFixedHeight ? ownMaxHeight : maxChildHeight;
newHeightSpec = MeasureSpec.makeMeasureSpec(ownHeight, MeasureSpec.EXACTLY);
for (View child : mMatchParentViews) {
- child.measure(widthMeasureSpec, newHeightSpec);
+ child.measure(getChildMeasureSpec(
+ widthMeasureSpec, 0 /* padding */, child.getLayoutParams().width),
+ newHeightSpec);
}
mMatchParentViews.clear();
int width = MeasureSpec.getSize(widthMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index de27119f..bfa74fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -21,6 +21,7 @@
import android.os.Process;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -52,35 +53,37 @@
for (int i = 0; i < n; i++) {
final StatusBarNotification sbn = mIntercepted.valueAt(i);
mReleased.add(sbn.getKey());
- mBar.addNotificationInternal(sbn, null);
+ mBar.displayNotification(sbn, null);
}
mIntercepted.clear();
updateSyntheticNotification();
}
- public boolean tryIntercept(StatusBarNotification notification, Ranking ranking) {
- if (ranking == null) return false;
+ public boolean tryIntercept(StatusBarNotification notification, RankingMap rankingMap) {
+ if (rankingMap == null) return false;
if (shouldDisplayIntercepted()) return false;
if (mReleased.contains(notification.getKey())) return false;
- if (!ranking.isInterceptedByDoNotDisturb(notification.getKey())) return false;
+ Ranking ranking = rankingMap.getRanking(notification.getKey());
+ if (!ranking.isInterceptedByDoNotDisturb()) return false;
mIntercepted.put(notification.getKey(), notification);
updateSyntheticNotification();
return true;
}
- public void retryIntercepts(Ranking ranking) {
+ public void retryIntercepts(RankingMap ranking) {
if (ranking == null) return;
- boolean changed = false;
final int N = mIntercepted.size();
+ final ArraySet<String> removed = new ArraySet<String>(N);
for (int i = 0; i < N; i++) {
final StatusBarNotification sbn = mIntercepted.valueAt(i);
if (!tryIntercept(sbn, ranking)) {
- changed = true;
- mBar.addNotificationInternal(sbn, ranking);
+ removed.add(sbn.getKey());
+ mBar.displayNotification(sbn, ranking);
}
}
- if (changed) {
+ if (!removed.isEmpty()) {
+ mIntercepted.removeAll(removed);
updateSyntheticNotification();
}
}
@@ -96,12 +99,6 @@
return ent.key.equals(mSynKey);
}
- public void update(StatusBarNotification notification) {
- if (mIntercepted.containsKey(notification.getKey())) {
- mIntercepted.put(notification.getKey(), notification);
- }
- }
-
private boolean shouldDisplayIntercepted() {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.DISPLAY_INTERCEPTED_NOTIFICATIONS, 0) != 0;
@@ -116,7 +113,7 @@
return;
}
final Notification n = new Notification.Builder(mContext)
- .setSmallIcon(R.drawable.ic_qs_zen_on)
+ .setSmallIcon(R.drawable.ic_notify_zen)
.setContentTitle(mContext.getResources().getQuantityString(
R.plurals.zen_mode_notification_title,
mIntercepted.size(), mIntercepted.size()))
@@ -129,7 +126,7 @@
mBar.getCurrentUserHandle());
if (mSynKey == null) {
mSynKey = sbn.getKey();
- mBar.addNotificationInternal(sbn, null);
+ mBar.displayNotification(sbn, null);
} else {
mBar.updateNotificationInternal(sbn, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
new file mode 100644
index 0000000..ce35e4b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -0,0 +1,190 @@
+/*
+ * 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 com.android.internal.app.IBatteryStats;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.view.View;
+
+/**
+ * Controls the little text indicator on the keyguard.
+ */
+public class KeyguardIndicationController {
+
+ private static final String TAG = "KeyguardIndicationController";
+
+ private static final int MSG_HIDE_TRANSIENT = 1;
+
+ private final Context mContext;
+ private final KeyguardIndicationTextView mTextView;
+ private final IBatteryStats mBatteryInfo;
+
+ private String mRestingIndication;
+ private String mTransientIndication;
+ private boolean mVisible;
+
+ private boolean mPowerPluggedIn;
+ private boolean mPowerCharged;
+
+ public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView) {
+ mContext = context;
+ mTextView = textView;
+
+ mBatteryInfo = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitor);
+ context.registerReceiverAsUser(
+ mReceiver, UserHandle.OWNER, new IntentFilter(Intent.ACTION_TIME_TICK), null, null);
+ }
+
+ public void setVisible(boolean visible) {
+ mVisible = visible;
+ mTextView.setVisibility(visible ? View.VISIBLE : View.GONE);
+ if (visible) {
+ hideTransientIndication();
+ updateIndication();
+ }
+ }
+
+ /**
+ * Sets the indication that is shown if nothing else is showing.
+ */
+ public void setRestingIndication(String restingIndication) {
+ mRestingIndication = restingIndication;
+ updateIndication();
+ }
+
+ /**
+ * Hides transient indication in {@param delayMs}.
+ */
+ public void hideTransientIndicationDelayed(long delayMs) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
+ }
+
+ /**
+ * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
+ */
+ public void showTransientIndication(int transientIndication) {
+ showTransientIndication(mContext.getResources().getString(transientIndication));
+ }
+
+ /**
+ * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
+ */
+ public void showTransientIndication(String transientIndication) {
+ mTransientIndication = transientIndication;
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ updateIndication();
+ }
+
+ /**
+ * Hides transient indication.
+ */
+ public void hideTransientIndication() {
+ if (mTransientIndication != null) {
+ mTransientIndication = null;
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ updateIndication();
+ }
+ }
+
+ private void updateIndication() {
+ if (mVisible) {
+ mTextView.switchIndication(computeIndication());
+ }
+ }
+
+ private String computeIndication() {
+ if (!TextUtils.isEmpty(mTransientIndication)) {
+ return mTransientIndication;
+ }
+ if (mPowerPluggedIn) {
+ return computePowerIndication();
+ }
+ return mRestingIndication;
+ }
+
+ private String computePowerIndication() {
+ if (mPowerCharged) {
+ return mContext.getResources().getString(R.string.keyguard_charged);
+ }
+
+ // Try fetching charging time from battery stats.
+ try {
+ long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
+ if (chargingTimeRemaining > 0) {
+ String chargingTimeFormatted =
+ Formatter.formatShortElapsedTime(mContext, chargingTimeRemaining);
+ return mContext.getResources().getString(
+ R.string.keyguard_indication_charging_time, chargingTimeFormatted);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IBatteryStats: ", e);
+ }
+
+ // Fall back to simple charging label.
+ return mContext.getResources().getString(R.string.keyguard_plugged_in);
+ }
+
+ KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
+ mPowerPluggedIn = status.status == BatteryManager.BATTERY_STATUS_CHARGING
+ || status.status == BatteryManager.BATTERY_STATUS_FULL;
+ mPowerCharged = status.isCharged();
+ updateIndication();
+ }
+ };
+
+ BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mVisible) {
+ updateIndication();
+ }
+ }
+ };
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
+ mTransientIndication = null;
+ updateIndication();
+ }
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index f9baecb..5cde979 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -106,7 +106,7 @@
private void selectLayout() {
if (mActualHeight <= mSmallHeight || mExpandedChild == null) {
- if (mContractedChild.getVisibility() != View.VISIBLE) {
+ if (mContractedChild != null && mContractedChild.getVisibility() != View.VISIBLE) {
mContractedChild.setVisibility(View.VISIBLE);
}
if (mExpandedChild != null && mExpandedChild.getVisibility() != View.INVISIBLE) {
@@ -116,7 +116,7 @@
if (mExpandedChild.getVisibility() != View.VISIBLE) {
mExpandedChild.setVisibility(View.VISIBLE);
}
- if (mContractedChild.getVisibility() != View.INVISIBLE) {
+ if (mContractedChild != null && mContractedChild.getVisibility() != View.INVISIBLE) {
mContractedChild.setVisibility(View.INVISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index d829ac0..9921c55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.view.View;
@@ -40,6 +41,9 @@
public View expandedPublic; // for insecure lockscreens
public View expandedBig;
private boolean interruption;
+ public boolean autoRedacted; // whether the redacted notification was generated by us
+ public boolean legacy; // whether the notification has a legacy, dark background
+
public Entry() {}
public Entry(StatusBarNotification n, StatusBarIconView ic) {
this.key = n.getKey();
@@ -64,15 +68,23 @@
public void setInterruption() {
interruption = true;
}
+
+ public boolean hasInterrupted() {
+ return interruption;
+ }
}
private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
- private Ranking mRanking;
+ private RankingMap mRanking;
private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
@Override
public int compare(Entry a, Entry b) {
if (mRanking != null) {
- return mRanking.getRank(a.key) - mRanking.getRank(b.key);
+ Ranking aRanking = mRanking.getRanking(a.key);
+ Ranking bRanking = mRanking.getRanking(b.key);
+ int aRank = aRanking != null ? aRanking.getRank() : -1;
+ int bRank = bRanking != null ? bRanking.getRank() : -1;
+ return aRank - bRank;
}
final StatusBarNotification na = a.notification;
@@ -105,12 +117,12 @@
return null;
}
- public void add(Entry entry, Ranking ranking) {
+ public void add(Entry entry, RankingMap ranking) {
mEntries.add(entry);
updateRankingAndSort(ranking);
}
- public Entry remove(String key, Ranking ranking) {
+ public Entry remove(String key, RankingMap ranking) {
Entry e = findByKey(key);
if (e == null) {
return null;
@@ -120,7 +132,7 @@
return e;
}
- public void updateRanking(Ranking ranking) {
+ public void updateRanking(RankingMap ranking) {
updateRankingAndSort(ranking);
}
@@ -134,12 +146,13 @@
}
}
} else {
- return mRanking.isAmbient(key);
+ Ranking ranking = mRanking.getRanking(key);
+ return ranking != null && ranking.isAmbient();
}
return false;
}
- private void updateRankingAndSort(Ranking ranking) {
+ private void updateRankingAndSort(RankingMap ranking) {
if (ranking != null) {
mRanking = ranking;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java
index ce31894..ce5ab5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java
@@ -18,15 +18,13 @@
import android.app.Notification;
import android.content.Context;
-import android.graphics.Color;
+import android.content.res.Configuration;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
-import android.view.View;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.IconMerger;
@@ -37,6 +35,8 @@
private TextView mMoreText;
private int mTintColor;
+ private int mIconSize;
+ private NotificationColorUtil mNotificationColorUtil = new NotificationColorUtil();
public NotificationOverflowIconsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -46,6 +46,8 @@
protected void onFinishInflate() {
super.onFinishInflate();
mTintColor = getResources().getColor(R.color.keyguard_overflow_content_color);
+ mIconSize = getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_icon_size);
}
public void setMoreText(TextView moreText) {
@@ -56,14 +58,24 @@
StatusBarIconView v = new StatusBarIconView(getContext(), "",
notification.notification.getNotification());
v.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- v.setColorFilter(mTintColor, PorterDuff.Mode.MULTIPLY);
- addView(v);
+ addView(v, mIconSize, mIconSize);
v.set(notification.icon.getStatusBarIcon());
+ applyColor(notification.notification.getNotification(), v);
updateMoreText();
}
+ private void applyColor(Notification notification, StatusBarIconView view) {
+ if (notification.color == Notification.COLOR_DEFAULT) {
+ if (mNotificationColorUtil.isGrayscale(view.getDrawable())) {
+ view.setColorFilter(mTintColor, PorterDuff.Mode.MULTIPLY);
+ }
+ } else {
+ view.setColorFilter(notification.color, PorterDuff.Mode.SRC_ATOP);
+ }
+ }
+
private void updateMoreText() {
- mMoreText.setText(getResources().getQuantityString(
- R.plurals.keyguard_more_overflow_text, getChildCount(), getChildCount()));
+ mMoreText.setText(
+ getResources().getString(R.string.keyguard_more_overflow_text, getChildCount()));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java
deleted file mode 100644
index 3ca021a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-/**
- * An single dot of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout}
- */
-public class SpeedBumpDotView extends View {
-
- private final Paint mPaint = new Paint();
-
- public SpeedBumpDotView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mPaint.setAntiAlias(true);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- float radius = getWidth() / 2.0f;
- canvas.drawCircle(radius, radius, radius, mPaint);
- }
-
- public void setColor(int color) {
- mPaint.setColor(color);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java
deleted file mode 100644
index cac6327..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-import android.view.View;
-import com.android.systemui.R;
-
-/**
- * The Algorithm of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} which can be
- * queried for {@link * com.android.systemui.statusbar.SpeedBumpDotsState}
- */
-public class SpeedBumpDotsAlgorithm {
-
- private final float mDotRadius;
-
- public SpeedBumpDotsAlgorithm(Context context) {
- mDotRadius = context.getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height)
- / 2.0f;
- }
-
- public void getState(SpeedBumpDotsState resultState) {
-
- // First reset the current state and ensure that every View has a ViewState
- resultState.resetViewStates();
-
- SpeedBumpDotsLayout hostView = resultState.getHostView();
- boolean currentlyVisible = hostView.isCurrentlyVisible();
- resultState.setActiveState(currentlyVisible
- ? SpeedBumpDotsState.SHOWN
- : SpeedBumpDotsState.HIDDEN);
- int hostWidth = hostView.getWidth();
- float layoutWidth = hostWidth - 2 * mDotRadius;
- int childCount = hostView.getChildCount();
- float paddingBetween = layoutWidth / (childCount - 1);
- float centerY = hostView.getHeight() / 2.0f;
- for (int i = 0; i < childCount; i++) {
- View child = hostView.getChildAt(i);
- SpeedBumpDotsState.ViewState viewState = resultState.getViewStateForView(child);
- if (currentlyVisible) {
- float xTranslation = i * paddingBetween;
- viewState.xTranslation = xTranslation;
- viewState.yTranslation = calculateYTranslation(hostView, centerY, xTranslation,
- layoutWidth);
- } else {
- viewState.xTranslation = layoutWidth / 2;
- viewState.yTranslation = centerY - mDotRadius;
- }
- viewState.alpha = currentlyVisible ? 1.0f : 0.0f;
- viewState.scale = currentlyVisible ? 1.0f : 0.5f;
- }
- }
-
- private float calculateYTranslation(SpeedBumpDotsLayout hostView, float centerY,
- float xTranslation, float layoutWidth) {
- float t = hostView.getAnimationProgress();
- if (t == 0.0f || t == 1.0f) {
- return centerY - mDotRadius;
- }
- float damping = (0.5f -Math.abs(0.5f - t)) * 1.3f;
- float partialOffset = xTranslation / layoutWidth;
- float indentFactor = (float) (Math.sin((t + partialOffset * 1.5f) * - Math.PI) * damping);
- return (1.0f - indentFactor) * centerY - mDotRadius;
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java
deleted file mode 100644
index ddf5215..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import android.animation.TimeAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import com.android.systemui.R;
-
-/**
- * A layout with a certain number of dots which are integrated in the
- * {@link com.android.systemui.statusbar.SpeedBumpView}
- */
-public class SpeedBumpDotsLayout extends ViewGroup {
-
- private static final float DOT_CLICK_ANIMATION_LENGTH = 300;
- private final int mDotSize;
- private final SpeedBumpDotsAlgorithm mAlgorithm = new SpeedBumpDotsAlgorithm(getContext());
- private final SpeedBumpDotsState mCurrentState = new SpeedBumpDotsState(this);
- private boolean mIsCurrentlyVisible = true;
- private final ValueAnimator mClickAnimator;
- private float mAnimationProgress;
- private ValueAnimator.AnimatorUpdateListener mClickUpdateListener
- = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mAnimationProgress = animation.getAnimatedFraction();
- updateChildren();
- }
- };
-
- public SpeedBumpDotsLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- mDotSize = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height);
- createDots(context, attrs);
- mClickAnimator = TimeAnimator.ofFloat(0, DOT_CLICK_ANIMATION_LENGTH);
- mClickAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
- mClickAnimator.addUpdateListener(mClickUpdateListener);
- }
-
- private void createDots(Context context, AttributeSet attrs) {
- SpeedBumpDotView blueDot = new SpeedBumpDotView(context, attrs);
- blueDot.setColor(getResources().getColor(R.color.speed_bump_dot_blue));
- addView(blueDot);
-
- SpeedBumpDotView redDot = new SpeedBumpDotView(context, attrs);
- redDot.setColor(getResources().getColor(R.color.speed_bump_dot_red));
- addView(redDot);
-
- SpeedBumpDotView yellowDot = new SpeedBumpDotView(context, attrs);
- yellowDot.setColor(getResources().getColor(R.color.speed_bump_dot_yellow));
- addView(yellowDot);
-
- SpeedBumpDotView greenDot = new SpeedBumpDotView(context, attrs);
- greenDot.setColor(getResources().getColor(R.color.speed_bump_dot_green));
- addView(greenDot);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int childWidthSpec = MeasureSpec.makeMeasureSpec(mDotSize,
- MeasureSpec.getMode(widthMeasureSpec));
- int childHeightSpec = MeasureSpec.makeMeasureSpec(mDotSize,
- MeasureSpec.getMode(heightMeasureSpec));
- measureChildren(childWidthSpec, childHeightSpec);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- child.layout(0, 0, mDotSize, mDotSize);
- }
- if (changed) {
- updateChildren();
- }
- }
-
- private void updateChildren() {
- mAlgorithm.getState(mCurrentState);
- mCurrentState.apply();
- }
-
- public void performVisibilityAnimation(boolean visible) {
- if (mClickAnimator.isRunning()) {
- mClickAnimator.cancel();
- }
- mIsCurrentlyVisible = visible;
- mAlgorithm.getState(mCurrentState);
- mCurrentState.animateToState();
- }
-
- public void setInvisible() {
- mIsCurrentlyVisible = false;
- mAlgorithm.getState(mCurrentState);
- mCurrentState.apply();
- }
-
- public boolean isCurrentlyVisible() {
- return mIsCurrentlyVisible;
- }
-
- public void performDotClickAnimation() {
- if (mClickAnimator.isRunning()) {
- // don't perform an animation if it's running already
- return;
- }
- mClickAnimator.start();
- }
-
-
- public float getAnimationProgress() {
- return mAnimationProgress;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java
deleted file mode 100644
index 06a7f95..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar;
-
-import android.view.View;
-import android.view.ViewPropertyAnimator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A state of a {@link com.android.systemui.statusbar.SpeedBumpDotsLayout}
- */
-public class SpeedBumpDotsState {
-
- public static final int HIDDEN = 1;
- public static final int SHOWN = 2;
- private static final int VISIBILITY_ANIMATION_DELAY_PER_ELEMENT = 80;
-
- private final SpeedBumpDotsLayout mHostView;
- private final HashMap<View, ViewState> mStateMap = new HashMap<View, ViewState>();
- private final Interpolator mFastOutSlowInInterpolator;
- private int mActiveState = 0;
-
- public SpeedBumpDotsState(SpeedBumpDotsLayout hostLayout) {
- mHostView = hostLayout;
- mFastOutSlowInInterpolator = AnimationUtils
- .loadInterpolator(hostLayout.getContext(),
- android.R.interpolator.fast_out_slow_in);
- }
-
- public SpeedBumpDotsLayout getHostView() {
- return mHostView;
- }
-
- public void resetViewStates() {
- int numChildren = mHostView.getChildCount();
- for (int i = 0; i < numChildren; i++) {
- View child = mHostView.getChildAt(i);
- ViewState viewState = mStateMap.get(child);
- if (viewState == null) {
- viewState = new ViewState();
- mStateMap.put(child, viewState);
- }
- }
- }
-
- public ViewState getViewStateForView(View requestedView) {
- return mStateMap.get(requestedView);
- }
-
- public void apply() {
- int childCount = mHostView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = mHostView.getChildAt(i);
- ViewState viewState = mStateMap.get(child);
- float translationX = child.getTranslationX();
- float translationY = child.getTranslationY();
- float scale = child.getScaleX();
- float alpha = child.getAlpha();
- if (translationX != viewState.xTranslation) {
- child.setTranslationX(viewState.xTranslation);
- }
- if (translationY != viewState.yTranslation) {
- child.setTranslationY(viewState.yTranslation);
- }
- if (scale != viewState.scale) {
- child.setScaleX(viewState.scale);
- child.setScaleY(viewState.scale);
- }
- if (alpha != viewState.alpha) {
- child.setAlpha(viewState.alpha);
- }
- }
- }
-
- public void animateToState() {
- int childCount = mHostView.getChildCount();
- int middleIndex = (childCount - 1) / 2;
- long delayPerElement = VISIBILITY_ANIMATION_DELAY_PER_ELEMENT;
- boolean isAppearing = getActiveState() == SHOWN;
- boolean isDisappearing = getActiveState() == HIDDEN;
- for (int i = 0; i < childCount; i++) {
- int delayIndex;
- if (i <= middleIndex) {
- delayIndex = i * 2;
- } else {
- int distToMiddle = i - middleIndex;
- delayIndex = (childCount - 1) - (distToMiddle - 1) * 2;
- }
- long startDelay = 0;
- if (isAppearing || isDisappearing) {
- if (isDisappearing) {
- delayIndex = childCount - 1 - delayIndex;
- }
- startDelay = delayIndex * delayPerElement;
- }
- View child = mHostView.getChildAt(i);
- ViewState viewState = mStateMap.get(child);
- child.animate().setInterpolator(mFastOutSlowInInterpolator)
- .setStartDelay(startDelay)
- .alpha(viewState.alpha).withLayer()
- .translationX(viewState.xTranslation)
- .translationY(viewState.yTranslation)
- .scaleX(viewState.scale).scaleY(viewState.scale);
- }
- }
-
- public int getActiveState() {
- return mActiveState;
- }
-
- public void setActiveState(int mActiveState) {
- this.mActiveState = mActiveState;
- }
-
- public static class ViewState {
- float xTranslation;
- float yTranslation;
- float alpha;
- float scale;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
index a84daef..f80f0fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
@@ -16,71 +16,26 @@
package com.android.systemui.statusbar;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.Outline;
import android.util.AttributeSet;
-import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.widget.TextView;
import com.android.systemui.R;
/**
* The view representing the separation between important and less important notifications
*/
-public class SpeedBumpView extends ExpandableView implements View.OnClickListener {
+public class SpeedBumpView extends ExpandableView {
- private final int mCollapsedHeight;
- private final int mDotsHeight;
- private final int mTextPaddingInset;
- private SpeedBumpDotsLayout mDots;
- private View mLineLeft;
- private View mLineRight;
- private boolean mIsExpanded;
- private boolean mDividerVisible = true;
- private ValueAnimator mCurrentAnimator;
+ private final int mSpeedBumpHeight;
+ private AlphaOptimizedView mLine;
+ private boolean mIsVisible = true;
private final Interpolator mFastOutSlowInInterpolator;
- private float mCenterX;
- private TextView mExplanationText;
- private boolean mExplanationTextVisible = false;
- private AnimatorListenerAdapter mHideExplanationListener = new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mCancelled) {
- mExplanationText.setVisibility(View.INVISIBLE);
- }
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- mCancelled = false;
- }
- };
- private Animator.AnimatorListener mAnimationFinishedListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mCurrentAnimator = null;
- }
- };
public SpeedBumpView(Context context, AttributeSet attrs) {
super(context, attrs);
- mCollapsedHeight = getResources()
- .getDimensionPixelSize(R.dimen.speed_bump_height_collapsed);
- mTextPaddingInset = getResources().getDimensionPixelSize(
- R.dimen.speed_bump_text_padding_inset);
- mDotsHeight = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height);
- setOnClickListener(this);
+ mSpeedBumpHeight = getResources()
+ .getDimensionPixelSize(R.dimen.speed_bump_height);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
android.R.interpolator.fast_out_slow_in);
}
@@ -88,112 +43,41 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mDots = (SpeedBumpDotsLayout) findViewById(R.id.speed_bump_dots_layout);
- mLineLeft = findViewById(R.id.speedbump_line_left);
- mLineRight = findViewById(R.id.speedbump_line_right);
- mExplanationText = (TextView) findViewById(R.id.speed_bump_text);
- resetExplanationText();
-
+ mLine = (AlphaOptimizedView) findViewById(R.id.speedbump_line);
}
@Override
protected int getInitialHeight() {
- return mCollapsedHeight;
+ return mSpeedBumpHeight;
}
@Override
public int getIntrinsicHeight() {
- if (mCurrentAnimator != null) {
- // expand animation is running
- return getActualHeight();
- }
- return mIsExpanded ? getHeight() : mCollapsedHeight;
+ return mSpeedBumpHeight;
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- Outline outline = new Outline();
- mCenterX = getWidth() / 2;
- float centerY = getHeight() / 2;
- // TODO: hide outline better
- // Temporary workaround to hide outline on a transparent view
- int outlineLeft = (int) (mCenterX - getResources().getDisplayMetrics().densityDpi * 8);
- int outlineTop = (int) (centerY - mDotsHeight / 2);
- outline.setOval(outlineLeft, outlineTop, outlineLeft + mDotsHeight,
- outlineTop + mDotsHeight);
- setOutline(outline);
- mLineLeft.setPivotX(mLineLeft.getWidth());
- mLineLeft.setPivotY(mLineLeft.getHeight() / 2);
- mLineRight.setPivotX(0);
- mLineRight.setPivotY(mLineRight.getHeight() / 2);
+ mLine.setPivotX(mLine.getWidth() / 2);
+ mLine.setPivotY(mLine.getHeight() / 2);
+ setOutline(null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
- int height = mCollapsedHeight + mExplanationText.getMeasuredHeight() - mTextPaddingInset;
+ int height = mSpeedBumpHeight;
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height);
}
@Override
- public void onClick(View v) {
- if (mCurrentAnimator != null) {
- return;
- }
- int startValue = mIsExpanded ? getMaxHeight() : mCollapsedHeight;
- int endValue = mIsExpanded ? mCollapsedHeight : getMaxHeight();
- mCurrentAnimator = ValueAnimator.ofInt(startValue, endValue);
- mCurrentAnimator.setInterpolator(mFastOutSlowInInterpolator);
- mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setActualHeight((int) animation.getAnimatedValue());
- }
- });
- mCurrentAnimator.addListener(mAnimationFinishedListener);
- mCurrentAnimator.start();
- mIsExpanded = !mIsExpanded;
- mDots.performDotClickAnimation();
- animateExplanationTextInternal(mIsExpanded);
- }
-
- private void animateExplanationTextInternal(boolean visible) {
- if (mExplanationTextVisible != visible) {
- float translationY = 0.0f;
- float scale = 0.5f;
- float alpha = 0.0f;
- boolean needsHideListener = true;
- if (visible) {
- mExplanationText.setVisibility(VISIBLE);
- translationY = mDots.getBottom() - mTextPaddingInset;
- scale = 1.0f;
- alpha = 1.0f;
- needsHideListener = false;
- }
- mExplanationText.animate().setInterpolator(mFastOutSlowInInterpolator)
- .alpha(alpha)
- .scaleX(scale)
- .scaleY(scale)
- .translationY(translationY)
- .setListener(needsHideListener ? mHideExplanationListener : null)
- .withLayer();
- mExplanationTextVisible = visible;
- }
- }
-
- @Override
public boolean isTransparent() {
return true;
}
public void performVisibilityAnimation(boolean nowVisible) {
animateDivider(nowVisible, null /* onFinishedRunnable */);
-
- // Animate explanation Text
- if (mIsExpanded) {
- animateExplanationTextInternal(nowVisible);
- }
}
/**
@@ -204,30 +88,16 @@
* finished.
*/
public void animateDivider(boolean nowVisible, Runnable onFinishedRunnable) {
- if (nowVisible != mDividerVisible) {
+ if (nowVisible != mIsVisible) {
// Animate dividers
float endValue = nowVisible ? 1.0f : 0.0f;
- float endTranslationXLeft = nowVisible ? 0.0f : mCenterX - mLineLeft.getRight();
- float endTranslationXRight = nowVisible ? 0.0f : mCenterX - mLineRight.getLeft();
- mLineLeft.animate()
+ mLine.animate()
.alpha(endValue)
- .withLayer()
.scaleX(endValue)
.scaleY(endValue)
- .translationX(endTranslationXLeft)
.setInterpolator(mFastOutSlowInInterpolator)
.withEndAction(onFinishedRunnable);
- mLineRight.animate()
- .alpha(endValue)
- .withLayer()
- .scaleX(endValue)
- .scaleY(endValue)
- .translationX(endTranslationXRight)
- .setInterpolator(mFastOutSlowInInterpolator);
-
- // Animate dots
- mDots.performVisibilityAnimation(nowVisible);
- mDividerVisible = nowVisible;
+ mIsVisible = nowVisible;
} else {
if (onFinishedRunnable != null) {
onFinishedRunnable.run();
@@ -236,34 +106,10 @@
}
public void setInvisible() {
- float endTranslationXLeft = mCenterX - mLineLeft.getRight();
- float endTranslationXRight = mCenterX - mLineRight.getLeft();
- mLineLeft.setAlpha(0.0f);
- mLineLeft.setScaleX(0.0f);
- mLineLeft.setScaleY(0.0f);
- mLineLeft.setTranslationX(endTranslationXLeft);
- mLineRight.setAlpha(0.0f);
- mLineRight.setScaleX(0.0f);
- mLineRight.setScaleY(0.0f);
- mLineRight.setTranslationX(endTranslationXRight);
- mDots.setInvisible();
- resetExplanationText();
-
- mDividerVisible = false;
- }
-
- public void collapse() {
- if (mIsExpanded) {
- setActualHeight(mCollapsedHeight);
- mIsExpanded = false;
- }
- resetExplanationText();
- }
-
- public void animateExplanationText(boolean nowVisible) {
- if (mIsExpanded) {
- animateExplanationTextInternal(nowVisible);
- }
+ mLine.setAlpha(0.0f);
+ mLine.setScaleX(0.0f);
+ mLine.setScaleY(0.0f);
+ mIsVisible = false;
}
@Override
@@ -275,17 +121,4 @@
public void performAddAnimation(long delay) {
performVisibilityAnimation(true);
}
-
- private void resetExplanationText() {
- mExplanationText.setTranslationY(0);
- mExplanationText.setVisibility(INVISIBLE);
- mExplanationText.setAlpha(0.0f);
- mExplanationText.setScaleX(0.5f);
- mExplanationText.setScaleY(0.5f);
- mExplanationTextVisible = false;
- }
-
- public boolean isExpanded() {
- return mIsExpanded;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
index 367d326..0fdc185 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
@@ -27,6 +27,7 @@
@Override
public float getInterpolation(float t) {
+ t *= 11f / 10f;
if (t < 4f / 11f) {
return SCALE_FACTOR * t * t;
} else if (t < 8f / 11f) {
@@ -36,8 +37,7 @@
float t2 = t - 9f / 11f;
return SCALE_FACTOR * t2 * t2 + 15f / 16f;
} else {
- float t2 = t - 21f / 22f;
- return SCALE_FACTOR * t2 * t2 + 63f / 64f;
+ return 1;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 6a83a5e..db85b14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -88,13 +88,15 @@
public void run(Result result) {
int y = getClockY() - mKeyguardStatusHeight/2;
- float clockAdjustment = getClockYExpansionAdjustment();
+ float topAdjustment = getTopExpansionAdjustment();
float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
- result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
+ result.stackScrollerPaddingAdjustment = (int) (topAdjustment*topPaddingAdjMultiplier);
int clockNotificationsPadding = getClockNotificationsPadding()
+ result.stackScrollerPaddingAdjustment;
int padding = y + clockNotificationsPadding;
- y += clockAdjustment;
+ if (mNotificationCount == 0) {
+ y += topAdjustment;
+ }
result.clockY = y;
result.stackScrollerPadding = mKeyguardStatusHeight + padding;
result.clockAlpha = getClockAlpha(result.stackScrollerPadding
@@ -117,8 +119,8 @@
return (int) (getClockYFraction() * mHeight);
}
- private float getClockYExpansionAdjustment() {
- float rubberbandFactor = getClockYExpansionRubberbandFactor();
+ private float getTopExpansionAdjustment() {
+ float rubberbandFactor = getTopExpansionRubberbandFactor();
float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
float t = value / mMaxPanelHeight;
float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
@@ -130,7 +132,7 @@
}
}
- private float getClockYExpansionRubberbandFactor() {
+ private float getTopExpansionRubberbandFactor() {
float t = getNotificationAmountT();
t = Math.min(t, 1.0f);
t = (float) Math.pow(t, 0.3f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
index b4f4865..ca49408 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
@@ -28,6 +28,7 @@
import android.view.ViewPropertyAnimator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -39,7 +40,10 @@
public class KeyguardPageSwipeHelper {
private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
- private static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.7f;
+ public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f;
+ public static final long HINT_PHASE1_DURATION = 250;
+ private static final long HINT_PHASE2_DURATION = 450;
+
private final Context mContext;
private FlingAnimationUtils mFlingAnimationUtils;
@@ -54,11 +58,13 @@
private int mTouchSlop;
private int mMinTranslationAmount;
private int mMinFlingVelocity;
+ private int mHintDistance;
private PowerManager mPowerManager;
private final View mLeftIcon;
private final View mCenterIcon;
private final View mRightIcon;
private Interpolator mFastOutSlowIn;
+ private Interpolator mBounceInterpolator;
private Animator mSwipeAnimator;
private boolean mCallbackCalled;
@@ -81,9 +87,12 @@
mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_min_swipe_amount);
+ mHintDistance =
+ mContext.getResources().getDimensionPixelSize(R.dimen.hint_move_distance_sideways);
mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.fast_out_slow_in);
+ mBounceInterpolator = new BounceInterpolator();
}
public boolean onTouchEvent(MotionEvent event) {
@@ -168,19 +177,105 @@
return false;
}
+ public void startHintAnimation(boolean right, Runnable onFinishedListener) {
+ startHintAnimationPhase1(right, onFinishedListener);
+ }
+
+ /**
+ * Phase 1: Move everything sidewards.
+ */
+ private void startHintAnimationPhase1(boolean right, final Runnable onFinishedListener) {
+ float target = right ? -mHintDistance : mHintDistance;
+ startHintTranslationAnimations(target, HINT_PHASE1_DURATION, mFastOutSlowIn);
+ ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mTranslation = (float) animation.getAnimatedValue();
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) {
+ mSwipeAnimator = null;
+ onFinishedListener.run();
+ } else {
+ startUnlockHintAnimationPhase2(onFinishedListener);
+ }
+ }
+ });
+ animator.setInterpolator(mFastOutSlowIn);
+ animator.setDuration(HINT_PHASE1_DURATION);
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ /**
+ * Phase 2: Move back.
+ */
+ private void startUnlockHintAnimationPhase2(final Runnable onFinishedListener) {
+ startHintTranslationAnimations(0f /* target */, HINT_PHASE2_DURATION, mBounceInterpolator);
+ ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, 0f);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mTranslation = (float) animation.getAnimatedValue();
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSwipeAnimator = null;
+ onFinishedListener.run();
+ }
+ });
+ animator.setInterpolator(mBounceInterpolator);
+ animator.setDuration(HINT_PHASE2_DURATION);
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ private void startHintTranslationAnimations(float target, long duration,
+ Interpolator interpolator) {
+ ArrayList<View> targetViews = mCallback.getTranslationViews();
+ int length = targetViews.size();
+ for (int i = 0; i < length; i++) {
+ View targetView = targetViews.get(i);
+ targetView.animate()
+ .setDuration(duration)
+ .setInterpolator(interpolator)
+ .translationX(target);
+ }
+ }
+
private void onUserActivity(long when) {
mPowerManager.userActivity(when, false);
}
private void cancelAnimations() {
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View target : targetViews) {
+ ArrayList<View> translatingViews = mCallback.getTranslationViews();
+ int length = translatingViews.size();
+ for (int i = 0; i < length; i++) {
+ View target = translatingViews.get(i);
+ target.animate().cancel();
+ }
+ ArrayList<View> fadingViews = mCallback.getFadingViews();
+ length = fadingViews.size();
+ for (int i = 0; i < length; i++) {
+ View target = fadingViews.get(i);
target.animate().cancel();
}
View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
targetView.animate().cancel();
if (mSwipeAnimator != null) {
- mSwipeAnimator.removeAllListeners();
mSwipeAnimator.cancel();
hideInactiveIcons(true);
}
@@ -206,6 +301,11 @@
// translation Animation
startTranslationAnimations(vel, target);
+ // fade animations
+ if (snapBack) {
+ startFadeInAnimations();
+ }
+
// animate left / right icon
startIconAnimation(vel, snapBack, target);
@@ -218,11 +318,18 @@
}
});
animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
@Override
public void onAnimationEnd(Animator animation) {
mSwipeAnimator = null;
mSwipingInProgress = false;
- if (!snapBack && !mCallbackCalled) {
+ if (!snapBack && !mCallbackCalled && !mCancelled) {
// ensure that the callback is called eventually
mCallback.onAnimationToSideStarted(mTranslation < 0);
@@ -254,9 +361,20 @@
mSwipeAnimator = animator;
}
+ private void startFadeInAnimations() {
+ ArrayList<View> fadingViews = mCallback.getFadingViews();
+ int length = fadingViews.size();
+ for (int i = 0; i < length; i++) {
+ View targetView = fadingViews.get(i);
+ targetView.animate().alpha(1.0f);
+ }
+ }
+
private void startTranslationAnimations(float vel, float target) {
ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View targetView : targetViews) {
+ int length = targetViews.size();
+ for (int i = 0; i < length; i++) {
+ View targetView = targetViews.get(i);
ViewPropertyAnimator animator = targetView.animate();
mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
animator.translationX(target);
@@ -281,10 +399,20 @@
private void setTranslation(float translation, boolean isReset) {
translation = rightSwipePossible() ? translation : Math.max(0, translation);
translation = leftSwipePossible() ? translation : Math.min(0, translation);
- if (translation != mTranslation) {
+ if (translation != mTranslation || isReset) {
ArrayList<View> translatedViews = mCallback.getTranslationViews();
- for (View view : translatedViews) {
- view.setTranslationX(translation);
+ int length = translatedViews.size();
+ for (int i = 0; i < length; i++) {
+ View target = translatedViews.get(i);
+ target.setTranslationX(translation);
+ }
+ float targetAlpha = 1.0f - Math.abs(translation / mMinTranslationAmount);
+ targetAlpha = Math.max(0.0f, targetAlpha);
+ ArrayList<View> fadingViews = mCallback.getFadingViews();
+ length = fadingViews.size();
+ for (int i = 0; i < length; i++) {
+ View view = fadingViews.get(i);
+ view.setAlpha(targetAlpha);
}
if (translation == 0.0f) {
boolean animate = !isReset;
@@ -307,7 +435,7 @@
}
}
- private void showAllIcons(boolean animate) {
+ public void showAllIcons(boolean animate) {
float scale = 1.0f;
float alpha = SWIPE_RESTING_ALPHA_AMOUNT;
updateIcon(mRightIcon, scale, alpha, animate);
@@ -315,6 +443,11 @@
updateIcon(mLeftIcon, scale, alpha, animate);
}
+ public void animateHideLeftRightIcon() {
+ updateIcon(mRightIcon, 0f, 0f, true);
+ updateIcon(mLeftIcon, 0f, 0f, true);
+ }
+
private void hideInactiveIcons(boolean animate){
View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon;
updateIcon(otherView, 0, 0, animate);
@@ -326,6 +459,7 @@
return;
}
if (!animate) {
+ view.animate().cancel();
view.setAlpha(alpha);
view.setScaleX(scale);
view.setScaleY(scale);
@@ -368,6 +502,13 @@
}
public void reset() {
+ if (mSwipeAnimator != null) {
+ mSwipeAnimator.cancel();
+ }
+ ArrayList<View> targetViews = mCallback.getTranslationViews();
+ for (View view : targetViews) {
+ view.animate().cancel();
+ }
setTranslation(0.0f, true);
mSwipingInProgress = false;
}
@@ -389,6 +530,8 @@
ArrayList<View> getTranslationViews();
+ ArrayList<View> getFadingViews();
+
View getLeftIcon();
View getCenterIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index dfd5a88..5c686fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -48,7 +48,7 @@
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
KeyguardPageSwipeHelper.Callback {
- private static float EXPANSION_RUBBER_BAND_EXTRA_FACTOR = 0.6f;
+ private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
private KeyguardPageSwipeHelper mPageSwiper;
private StatusBarHeaderView mHeader;
@@ -82,17 +82,20 @@
private float mQsExpansionHeight;
private int mQsMinExpansionHeight;
private int mQsMaxExpansionHeight;
- private int mMinStackHeight;
private int mQsPeekHeight;
- private float mNotificationTranslation;
- private int mStackScrollerIntrinsicPadding;
private boolean mStackScrollerOverscrolling;
private boolean mQsExpansionEnabled = true;
private ValueAnimator mQsExpansionAnimator;
private FlingAnimationUtils mFlingAnimationUtils;
private int mStatusBarMinHeight;
+ private boolean mHeaderHidden;
+ private boolean mUnlockIconActive;
+ private int mNotificationsHeaderCollideDistance;
+ private int mUnlockMoveDistance;
private Interpolator mFastOutSlowInInterpolator;
+ private Interpolator mFastOutLinearInterpolator;
+ private Interpolator mLinearOutSlowInInterpolator;
private ObjectAnimator mClockAnimator;
private int mClockAnimationTarget = -1;
private int mTopPaddingAdjustment;
@@ -105,6 +108,7 @@
private KeyguardBottomAreaView mKeyguardBottomArea;
private boolean mBlockTouches;
private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
+ private ArrayList<View> mSwipeFadingViews = new ArrayList<>();
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -143,9 +147,13 @@
mNotificationStackScroller.setOverscrollTopChangedListener(this);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
android.R.interpolator.fast_out_slow_in);
+ mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
+ android.R.interpolator.linear_out_slow_in);
+ mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(),
+ android.R.interpolator.fast_out_linear_in);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
mSwipeTranslationViews.add(mNotificationStackScroller);
- mSwipeTranslationViews.add(mKeyguardStatusView);
+ mSwipeFadingViews.add(mKeyguardStatusView);
mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
}
@@ -154,11 +162,13 @@
super.loadDimens();
mNotificationTopPadding = getResources().getDimensionPixelSize(
R.dimen.notifications_top_padding);
- mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.4f);
mStatusBarMinHeight = getResources().getDimensionPixelSize(
com.android.internal.R.dimen.status_bar_height);
mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height);
+ mNotificationsHeaderCollideDistance =
+ getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
+ mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
mClockPositionAlgorithm.loadDimens(getResources());
}
@@ -171,7 +181,8 @@
mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
if (mQsExpanded) {
if (mQsFullyExpanded) {
- setQsStackScrollerPadding(mQsMaxExpansionHeight);
+ mQsExpansionHeight = mQsMaxExpansionHeight;
+ requestScrollerTopPaddingUpdate(false /* animate */);
}
} else {
if (!mStackScrollerOverscrolling) {
@@ -188,11 +199,12 @@
*/
private void positionClockAndNotifications() {
boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+ int stackScrollerPadding;
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
int bottom = mStackScrollerOverscrolling
? mHeader.getCollapsedHeight()
: mHeader.getBottom();
- mStackScrollerIntrinsicPadding = bottom + mQsPeekHeight
+ stackScrollerPadding = bottom + mQsPeekHeight
+ mNotificationTopPadding;
mTopPaddingAdjustment = 0;
} else {
@@ -210,12 +222,11 @@
mKeyguardStatusView.setY(mClockPositionResult.clockY);
}
applyClockAlpha(mClockPositionResult.clockAlpha);
- mStackScrollerIntrinsicPadding = mClockPositionResult.stackScrollerPadding;
+ stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
}
- mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
- mAnimateNextTopPaddingChange || animateClock);
- mAnimateNextTopPaddingChange = false;
+ mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
+ requestScrollerTopPaddingUpdate(animateClock);
}
private void startClockAnimation(int y) {
@@ -231,8 +242,8 @@
mClockAnimator.removeAllListeners();
mClockAnimator.cancel();
}
- mClockAnimator =
- ObjectAnimator.ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget);
+ mClockAnimator = ObjectAnimator
+ .ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget);
mClockAnimator.setInterpolator(mFastOutSlowInInterpolator);
mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
mClockAnimator.addListener(new AnimatorListenerAdapter() {
@@ -277,6 +288,7 @@
@Override
public void resetViews() {
mBlockTouches = false;
+ mUnlockIconActive = false;
mPageSwiper.reset();
closeQs();
}
@@ -369,6 +381,7 @@
mInitialTouchX = x;
mQsTracking = true;
mIntercepting = false;
+ mNotificationStackScroller.removeLongPressCallback();
return true;
}
break;
@@ -411,7 +424,9 @@
}
// TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
// implementation.
- if (!mIsExpanding && !mQsExpanded && mStatusBar.getBarState() != StatusBarState.SHADE) {
+ if ((!mIsExpanding || mHintAnimationRunning)
+ && !mQsExpanded
+ && mStatusBar.getBarState() != StatusBarState.SHADE) {
mPageSwiper.onTouchEvent(event);
if (mPageSwiper.isSwipingInProgress()) {
return true;
@@ -506,6 +521,13 @@
updateQsState();
}
+ @Override
+ public void flingTopOverscroll(float velocity, boolean open) {
+ mStackScrollerOverscrolling = false;
+ setQsExpansion(mQsExpansionHeight);
+ flingSettings(velocity, open);
+ }
+
private void onQsExpansionStarted() {
onQsExpansionStarted(0);
}
@@ -537,10 +559,11 @@
mHeader.setExpanded(expandVisually, mStackScrollerOverscrolling);
mNotificationStackScroller.setEnabled(!mQsExpanded);
mQsPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
- mQsContainer.setVisibility(mKeyguardShowing && !mQsExpanded
+ mQsContainer.setVisibility(mKeyguardShowing && !expandVisually
? View.INVISIBLE
: View.VISIBLE);
mScrollView.setTouchEnabled(mQsExpanded);
+ mNotificationStackScroller.setTouchEnabled(!mQsExpanded);
}
private void setQsExpansion(float height) {
@@ -554,9 +577,7 @@
mQsExpansionHeight = height;
mHeader.setExpansion(height - mQsPeekHeight);
setQsTranslation(height);
- if (!mStackScrollerOverscrolling) {
- setQsStackScrollerPadding(height);
- }
+ requestScrollerTopPaddingUpdate(false /* animate */);
mStatusBar.userActivity();
}
@@ -564,24 +585,12 @@
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 requestScrollerTopPaddingUpdate(boolean animate) {
+ mNotificationStackScroller.updateTopPadding(mQsExpansionHeight,
+ mScrollView.getScrollY(),
+ mAnimateNextTopPaddingChange || animate);
+ mAnimateNextTopPaddingChange = false;
}
private void trackMovement(MotionEvent event) {
@@ -687,13 +696,16 @@
}
@Override
+ protected boolean hasNotifications() {
+ return mNotificationStackScroller.getNotGoneChildCount() > 0;
+ }
+
+ @Override
protected int getMaxPanelHeight() {
// TODO: Figure out transition for collapsing when QS is open, adjust height here.
- int maxPanelHeight = super.getMaxPanelHeight();
- int emptyBottomMargin = mStackScrollerContainer.getHeight()
- - mNotificationStackScroller.getHeight()
- + mNotificationStackScroller.getEmptyBottomMargin();
- int maxHeight = maxPanelHeight - emptyBottomMargin - mTopPaddingAdjustment;
+ int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
+ int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin
+ - mTopPaddingAdjustment;
maxHeight = Math.max(maxHeight, mStatusBarMinHeight);
return maxHeight;
}
@@ -708,6 +720,89 @@
positionClockAndNotifications();
}
mNotificationStackScroller.setStackHeight(expandedHeight);
+ updateKeyguardHeaderVisibility();
+ updateUnlockIcon();
+ }
+
+ @Override
+ protected float getOverExpansionAmount() {
+ return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
+ }
+
+ @Override
+ protected float getOverExpansionPixels() {
+ return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
+ }
+
+ private void updateUnlockIcon() {
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
+ || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+ boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance;
+ if (active && !mUnlockIconActive && mTracking) {
+ mKeyguardBottomArea.getLockIcon().animate()
+ .alpha(1f)
+ .scaleY(LOCK_ICON_ACTIVE_SCALE)
+ .scaleX(LOCK_ICON_ACTIVE_SCALE)
+ .setInterpolator(mFastOutLinearInterpolator)
+ .setDuration(150);
+ } else if (!active && mUnlockIconActive && mTracking) {
+ mKeyguardBottomArea.getLockIcon().animate()
+ .alpha(KeyguardPageSwipeHelper.SWIPE_RESTING_ALPHA_AMOUNT)
+ .scaleY(1f)
+ .scaleX(1f)
+ .setInterpolator(mFastOutLinearInterpolator)
+ .setDuration(150);
+ }
+ mUnlockIconActive = active;
+ }
+ }
+
+ /**
+ * Hides the header when notifications are colliding with it.
+ */
+ private void updateKeyguardHeaderVisibility() {
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
+ || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+ boolean hidden;
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+
+ // When on Keyguard, we hide the header as soon as the top card of the notification
+ // stack scroller is close enough (collision distance) to the bottom of the header.
+ hidden = mNotificationStackScroller.getNotificationsTopY()
+ <= mHeader.getBottom() + mNotificationsHeaderCollideDistance;
+ } else {
+
+ // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
+ // soon as we start translating the stack.
+ hidden = mNotificationStackScroller.getTranslationY() < 0;
+ }
+
+ if (hidden && !mHeaderHidden) {
+ mHeader.animate()
+ .alpha(0f)
+ .withLayer()
+ .translationY(-mHeader.getHeight()/2)
+ .setInterpolator(mFastOutLinearInterpolator)
+ .setDuration(200);
+ } else if (!hidden && mHeaderHidden) {
+ mHeader.animate()
+ .alpha(1f)
+ .withLayer()
+ .translationY(0)
+ .setInterpolator(mLinearOutSlowInInterpolator)
+ .setDuration(200);
+ }
+ mHeaderHidden = hidden;
+ } else {
+ mHeader.animate().cancel();
+ mHeader.setAlpha(1f);
+ mHeader.setTranslationY(0f);
+ if (mHeader.getLayerType() != LAYER_TYPE_NONE) {
+ mHeader.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ mHeaderHidden = false;
+ }
+
}
@Override
@@ -725,25 +820,51 @@
}
@Override
- protected void onOverExpansionChanged(float overExpansion) {
- float currentOverScroll = mNotificationStackScroller.getCurrentOverScrolledPixels(true);
- float expansionChange = overExpansion - mOverExpansion;
- expansionChange *= EXPANSION_RUBBER_BAND_EXTRA_FACTOR;
- mNotificationStackScroller.setOverScrolledPixels(currentOverScroll + expansionChange,
- true /* onTop */,
- false /* animate */);
- super.onOverExpansionChanged(overExpansion);
+ protected void setOverExpansion(float overExpansion, boolean isPixels) {
+ if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
+ mNotificationStackScroller.setOnHeightChangedListener(null);
+ if (isPixels) {
+ mNotificationStackScroller.setOverScrolledPixels(
+ overExpansion, true /* onTop */, false /* animate */);
+ } else {
+ mNotificationStackScroller.setOverScrollAmount(
+ overExpansion, true /* onTop */, false /* animate */);
+ }
+ mNotificationStackScroller.setOnHeightChangedListener(this);
+ }
+ }
+
+ @Override
+ protected void onTrackingStarted() {
+ super.onTrackingStarted();
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
+ || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
+ mPageSwiper.animateHideLeftRightIcon();
+ }
}
@Override
protected void onTrackingStopped(boolean expand) {
super.onTrackingStopped(expand);
- mOverExpansion = 0.0f;
- mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
- true /* animate */);
+ if (expand) {
+ mNotificationStackScroller.setOverScrolledPixels(
+ 0.0f, true /* onTop */, true /* animate */);
+ }
+ if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
+ || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
+ mPageSwiper.showAllIcons(true);
+ }
+ if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
+ || mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
+ mKeyguardBottomArea.getLockIcon().animate()
+ .alpha(0f)
+ .scaleX(2f)
+ .scaleY(2f)
+ .setInterpolator(mFastOutLinearInterpolator)
+ .setDuration(100);
+ }
}
-
@Override
public void onHeightChanged(ExpandableView view) {
requestPanelHeightUpdate();
@@ -752,8 +873,7 @@
@Override
public void onScrollChanged() {
if (mQsExpanded) {
- mNotificationStackScroller.setTranslationY(
- mNotificationTranslation - mScrollView.getScrollY());
+ requestScrollerTopPaddingUpdate(false /* animate */);
}
}
@@ -777,14 +897,61 @@
@Override
public void onAnimationToSideStarted(boolean rightPage) {
- if (rightPage) {
- mKeyguardBottomArea.launchCamera();
- } else {
+ boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage;
+ if (start) {
mKeyguardBottomArea.launchPhone();
+ } else {
+ mKeyguardBottomArea.launchCamera();
}
mBlockTouches = true;
}
+ @Override
+ protected void onEdgeClicked(boolean right) {
+ if ((right && getRightIcon().getVisibility() != View.VISIBLE)
+ || (!right && getLeftIcon().getVisibility() != View.VISIBLE)) {
+ return;
+ }
+ mHintAnimationRunning = true;
+ mPageSwiper.startHintAnimation(right, new Runnable() {
+ @Override
+ public void run() {
+ mHintAnimationRunning = false;
+ mStatusBar.onHintFinished();
+ }
+ });
+ startHighlightIconAnimation(right ? getRightIcon() : getLeftIcon());
+ boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? right : !right;
+ if (start) {
+ mStatusBar.onPhoneHintStarted();
+ } else {
+ mStatusBar.onCameraHintStarted();
+ }
+ }
+
+ @Override
+ protected void startUnlockHintAnimation() {
+ super.startUnlockHintAnimation();
+ startHighlightIconAnimation(getCenterIcon());
+ }
+
+ /**
+ * Starts the highlight (making it fully opaque) animation on an icon.
+ */
+ private void startHighlightIconAnimation(final View icon) {
+ icon.animate()
+ .alpha(1.0f)
+ .setDuration(KeyguardPageSwipeHelper.HINT_PHASE1_DURATION)
+ .setInterpolator(mFastOutSlowInInterpolator)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ icon.animate().alpha(KeyguardPageSwipeHelper.SWIPE_RESTING_ALPHA_AMOUNT)
+ .setDuration(KeyguardPageSwipeHelper.HINT_PHASE1_DURATION)
+ .setInterpolator(mFastOutSlowInInterpolator);
+ }
+ });
+ }
@Override
public float getPageWidth() {
@@ -797,8 +964,15 @@
}
@Override
+ public ArrayList<View> getFadingViews() {
+ return mSwipeFadingViews;
+ }
+
+ @Override
public View getLeftIcon() {
- return mKeyguardBottomArea.getPhoneImageView();
+ return getLayoutDirection() == LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getCameraImageView()
+ : mKeyguardBottomArea.getPhoneImageView();
}
@Override
@@ -808,6 +982,8 @@
@Override
public View getRightIcon() {
- return mKeyguardBottomArea.getCameraImageView();
+ return getLayoutDirection() == LAYOUT_DIRECTION_RTL
+ ? mKeyguardBottomArea.getPhoneImageView()
+ : mKeyguardBottomArea.getCameraImageView();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 4686933..08305dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -27,8 +27,11 @@
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
@@ -41,7 +44,8 @@
public abstract class PanelView extends FrameLayout {
public static final boolean DEBUG = PanelBar.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
- protected float mOverExpansion;
+
+ private static final long KEYGUARD_MOVE_UP_LENGTH = 300;
private final void logf(String fmt, Object... args) {
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -50,21 +54,32 @@
protected PhoneStatusBar mStatusBar;
private float mPeekHeight;
private float mHintDistance;
+ private int mEdgeTapAreaWidth;
private float mInitialOffsetOnTouch;
private float mExpandedFraction = 0;
- private float mExpandedHeight = 0;
+ protected float mExpandedHeight = 0;
private boolean mJustPeeked;
private boolean mClosing;
- private boolean mTracking;
+ protected boolean mTracking;
private boolean mTouchSlopExceeded;
private int mTrackingPointer;
protected int mTouchSlop;
+ protected boolean mHintAnimationRunning;
+ private boolean mOverExpandedBeforeFling;
+ private boolean mKeyguardMovingUp;
+ private int mKeyguardMoveUpDistance;
+ private float mKeyguardFingerHeight;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
private VelocityTrackerInterface mVelocityTracker;
private FlingAnimationUtils mFlingAnimationUtils;
+ /**
+ * Whether an instant expand request is currently pending and we are just waiting for layout.
+ */
+ private boolean mInstantExpanding;
+
PanelBar mBar;
protected int mMaxPanelHeight = -1;
@@ -74,6 +89,8 @@
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mBounceInterpolator;
+ private Interpolator mKeyguardMoveUpInterpolator;
+ private final Interpolator mLinearInterpolator = new LinearInterpolator();
protected void onExpandingFinished() {
mBar.onExpandingFinished();
@@ -101,6 +118,7 @@
mLinearOutSlowInInterpolator =
AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mBounceInterpolator = new BounceInterpolator();
+ mKeyguardMoveUpInterpolator = new PathInterpolator(0.6f, 0f, 0.4f, 1f);
}
protected void loadDimens() {
@@ -111,6 +129,9 @@
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mHintDistance = res.getDimension(R.dimen.hint_move_distance);
+ mEdgeTapAreaWidth = res.getDimensionPixelSize(R.dimen.edge_tap_area_width);
+ mKeyguardMoveUpDistance =
+ res.getDimensionPixelSize(R.dimen.keyguard_panel_move_up_distance);
}
private void trackMovement(MotionEvent event) {
@@ -125,6 +146,9 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (mInstantExpanding) {
+ return false;
+ }
/*
* We capture touch events here and update the expand height here in case according to
@@ -147,7 +171,6 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
-
mInitialTouchY = y;
mInitialTouchX = x;
mInitialOffsetOnTouch = mExpandedHeight;
@@ -156,10 +179,11 @@
initVelocityTracker();
}
trackMovement(event);
- if (!waitForTouchSlop || mHeightAnimator != null) {
+ if (!waitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)) {
if (mHeightAnimator != null) {
mHeightAnimator.cancel(); // end any outstanding animations
}
+ mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning);
onTrackingStarted();
}
if (mExpandedHeight == 0) {
@@ -205,8 +229,13 @@
mJustPeeked = false;
}
if (!mJustPeeked && (!waitForTouchSlop || mTracking)) {
- setExpandedHeightInternal(newHeight);
- mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
+ if (mStatusBar.getBarState() == StatusBarState.KEYGUARD &&
+ !hasNotifications()) {
+ setExpandedHeightKeyguard(newHeight);
+ } else {
+ setExpandedHeightInternal(newHeight);
+ mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
+ }
}
trackMovement(event);
@@ -216,13 +245,14 @@
case MotionEvent.ACTION_CANCEL:
mTrackingPointer = -1;
trackMovement(event);
- if (mTracking && mTouchSlopExceeded) {
+ if ((mTracking && mTouchSlopExceeded)
+ || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
float vel = getCurrentVelocity();
boolean expand = flingExpands(vel);
onTrackingStopped(expand);
fling(vel, expand);
} else {
- boolean expands = onEmptySpaceClick();
+ boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
if (mVelocityTracker != null) {
@@ -234,10 +264,56 @@
return !waitForTouchSlop || mTracking;
}
+ protected abstract boolean hasNotifications();
+
+ private void setExpandedHeightKeyguard(float newHeight) {
+ mKeyguardFingerHeight = newHeight;
+ if (newHeight < getMaxPanelHeight() && !mKeyguardMovingUp) {
+ mKeyguardMovingUp = true;
+ ValueAnimator anim = createHeightAnimator(
+ getMaxPanelHeight() - mKeyguardMoveUpDistance);
+ anim.setDuration(KEYGUARD_MOVE_UP_LENGTH);
+ anim.setInterpolator(mKeyguardMoveUpInterpolator);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHeightAnimator = null;
+ }
+ });
+ mHeightAnimator = anim;
+ anim.start();
+ postOnAnimationDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (mKeyguardFingerHeight < mExpandedHeight && mHeightAnimator != null
+ && mKeyguardMovingUp) {
+ mHeightAnimator.cancel();
+ float target = getMaxPanelHeight() - 1.75f * mKeyguardMoveUpDistance;
+ float diff = mExpandedHeight - target;
+ ValueAnimator anim = createHeightAnimator(target);
+ float velocity = 2.5f * mKeyguardMoveUpDistance /
+ (KEYGUARD_MOVE_UP_LENGTH / 1000f);
+ anim.setInterpolator(mLinearInterpolator);
+ anim.setDuration(Math.max(0, (long) (diff / velocity * 1000f)));
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHeightAnimator = null;
+ }
+ });
+ mHeightAnimator = anim;
+ anim.start();
+ }
+ }
+ }, KEYGUARD_MOVE_UP_LENGTH / 2);
+ }
+ }
+
protected abstract boolean hasConflictingGestures();
protected void onTrackingStopped(boolean expand) {
mTracking = false;
+ mKeyguardMovingUp = false;
mBar.onTrackingStopped(PanelView.this, expand);
}
@@ -259,6 +335,9 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mInstantExpanding) {
+ return false;
+ }
/*
* If the user drags anywhere inside the panel we intercept it if he moves his finger
@@ -279,8 +358,9 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- if (mHeightAnimator != null) {
+ if (mHeightAnimator != null && !mHintAnimationRunning) {
mHeightAnimator.cancel(); // end any outstanding animations
+ mTouchSlopExceeded = true;
return true;
}
mInitialTouchY = y;
@@ -305,6 +385,9 @@
trackMovement(event);
if (scrolledToBottom) {
if (h < -mTouchSlop && h < -Math.abs(x - mInitialTouchX)) {
+ if (mHeightAnimator != null) {
+ mHeightAnimator.cancel();
+ }
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = y;
mInitialTouchX = x;
@@ -361,12 +444,16 @@
protected void fling(float vel, boolean expand) {
cancelPeek();
+ if (mHeightAnimator != null) {
+ mHeightAnimator.cancel();
+ }
float target = expand ? getMaxPanelHeight() : 0.0f;
- if (target == mExpandedHeight) {
+ if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
onExpandingFinished();
mBar.panelExpansionChanged(this, mExpandedFraction);
return;
}
+ mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
ValueAnimator animator = createHeightAnimator(target);
if (expand) {
mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
@@ -386,8 +473,8 @@
onExpandingFinished();
}
});
- animator.start();
mHeightAnimator = animator;
+ animator.start();
}
@Override
@@ -423,7 +510,7 @@
public void setExpandedHeight(float height) {
if (DEBUG) logf("setExpandedHeight(%.1f)", height);
- setExpandedHeightInternal(height);
+ setExpandedHeightInternal(height + getOverExpansionPixels());
mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
}
@@ -441,33 +528,39 @@
// If the user isn't actively poking us, let's update the height
if (!mTracking && mHeightAnimator == null
&& mExpandedHeight > 0 && currentMaxPanelHeight != mExpandedHeight) {
- setExpandedHeightInternal(currentMaxPanelHeight);
+ setExpandedHeight(currentMaxPanelHeight);
}
}
public void setExpandedHeightInternal(float h) {
- float fh = getMaxPanelHeight();
- mExpandedHeight = Math.max(0, Math.min(fh, h));
- float overExpansion = h - fh;
- overExpansion = Math.max(0, overExpansion);
- if (overExpansion != mOverExpansion) {
- onOverExpansionChanged(overExpansion);
- }
-
- if (DEBUG) {
- logf("setExpansion: height=%.1f fh=%.1f tracking=%s", h, fh, mTracking ? "T" : "f");
+ float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
+ if (mHeightAnimator == null) {
+ float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
+ if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
+ setOverExpansion(overExpansionPixels, true /* isPixels */);
+ }
+ mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
+ } else {
+ mExpandedHeight = h;
+ if (mOverExpandedBeforeFling) {
+ setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
+ }
}
onHeightUpdated(mExpandedHeight);
- mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : mExpandedHeight / fh);
+ mExpandedFraction = Math.min(1f, fhWithoutOverExpansion == 0
+ ? 0
+ : mExpandedHeight / fhWithoutOverExpansion);
}
- protected void onOverExpansionChanged(float overExpansion) {
- mOverExpansion = overExpansion;
- }
+ protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
protected abstract void onHeightUpdated(float expandedHeight);
+ protected abstract float getOverExpansionAmount();
+
+ protected abstract float getOverExpansionPixels();
+
/**
* This returns the maximum height of the panel. Children should override this if their
* desired height is not the full height.
@@ -541,6 +634,41 @@
}
}
+ public void instantExpand() {
+ mInstantExpanding = true;
+ abortAnimations();
+ if (mTracking) {
+ onTrackingStopped(true /* expands */); // The panel is expanded after this call.
+ onExpandingFinished();
+ }
+ setVisibility(VISIBLE);
+
+ // Wait for window manager to pickup the change, so we know the maximum height of the panel
+ // then.
+ getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ if (mStatusBar.getStatusBarWindow().getHeight()
+ != mStatusBar.getStatusBarHeight()) {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ setExpandedFraction(1f);
+ mInstantExpanding = false;
+ }
+ }
+ });
+
+ // Make sure a layout really happens.
+ requestLayout();
+ }
+
+ private void abortAnimations() {
+ cancelPeek();
+ if (mHeightAnimator != null) {
+ mHeightAnimator.cancel();
+ }
+ }
+
protected void startUnlockHintAnimation() {
// We don't need to hint the user if an animation is already running or the user is changing
@@ -550,14 +678,22 @@
}
cancelPeek();
onExpandingStarted();
- startUnlockHintAnimationPhase1();
+ startUnlockHintAnimationPhase1(new Runnable() {
+ @Override
+ public void run() {
+ onExpandingFinished();
+ mStatusBar.onHintFinished();
+ mHintAnimationRunning = false;
+ }
+ });
mStatusBar.onUnlockHintStarted();
+ mHintAnimationRunning = true;
}
/**
* Phase 1: Move everything upwards.
*/
- private void startUnlockHintAnimationPhase1() {
+ private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
ValueAnimator animator = createHeightAnimator(target);
animator.setDuration(250);
@@ -574,10 +710,9 @@
public void onAnimationEnd(Animator animation) {
if (mCancelled) {
mHeightAnimator = null;
- onExpandingFinished();
- mStatusBar.onUnlockHintFinished();
+ onAnimationFinished.run();
} else {
- startUnlockHintAnimationPhase2();
+ startUnlockHintAnimationPhase2(onAnimationFinished);
}
}
});
@@ -588,7 +723,7 @@
/**
* Phase 2: Bounce down.
*/
- private void startUnlockHintAnimationPhase2() {
+ private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
animator.setDuration(450);
animator.setInterpolator(mBounceInterpolator);
@@ -596,8 +731,7 @@
@Override
public void onAnimationEnd(Animator animation) {
mHeightAnimator = null;
- onExpandingFinished();
- mStatusBar.onUnlockHintFinished();
+ onAnimationFinished.run();
}
});
animator.start();
@@ -609,7 +743,8 @@
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- setExpandedHeight((Float) animation.getAnimatedValue());
+ setExpandedHeightInternal((Float) animation.getAnimatedValue());
+ mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
}
});
return animator;
@@ -620,13 +755,30 @@
*
* @return whether the panel will be expanded after the action performed by this method
*/
- private boolean onEmptySpaceClick() {
+ private boolean onEmptySpaceClick(float x) {
+ if (mHintAnimationRunning) {
+ return true;
+ }
+ if (x < mEdgeTapAreaWidth
+ && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ onEdgeClicked(false /* right */);
+ return true;
+ } else if (x > getWidth() - mEdgeTapAreaWidth
+ && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ onEdgeClicked(true /* right */);
+ return true;
+ } else {
+ return onMiddleClicked();
+ }
+ }
+
+ private boolean onMiddleClicked() {
switch (mStatusBar.getBarState()) {
case StatusBarState.KEYGUARD:
startUnlockHintAnimation();
return true;
case StatusBarState.SHADE_LOCKED:
- // TODO: Go to Keyguard again.
+ mStatusBar.goToKeyguard();
return true;
case StatusBarState.SHADE:
collapse();
@@ -636,6 +788,8 @@
}
}
+ protected abstract void onEdgeClicked(boolean right);
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
+ " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
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 b1216e69..b23992d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -64,7 +64,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
-import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.DisplayMetrics;
@@ -77,15 +77,19 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewPropertyAnimator;
+import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -99,12 +103,14 @@
import com.android.systemui.qs.CircularClipper;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.InterceptedNotifications;
+import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.NotificationOverflowContainer;
@@ -122,7 +128,6 @@
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
@@ -174,6 +179,11 @@
/** The minimum delay in ms between reports of notification visibility. */
private static final int VISIBILITY_REPORT_MIN_DELAY_MS = 500;
+ /**
+ * The delay to reset the hint text when the hint animation is finished running.
+ */
+ private static final int HINT_RESET_DELAY_MS = 1200;
+
// fling gesture tuning parameters, scaled to display density
private float mSelfExpandVelocityPx; // classic value: 2000px/s
private float mSelfCollapseVelocityPx; // classic value: 2000px/s (will be negated to collapse "up")
@@ -248,10 +258,8 @@
View mKeyguardStatusView;
KeyguardBottomAreaView mKeyguardBottomArea;
boolean mLeaveOpenOnKeyguardHide;
- KeyguardIndicationTextView mKeyguardIndicationTextView;
+ KeyguardIndicationController mKeyguardIndicationController;
- // TODO: Fetch phrase from search/hotword provider.
- String mKeyguardHotwordPhrase = "";
int mKeyguardMaxNotificationCount;
View mDateTimeView;
@@ -259,7 +267,6 @@
private TextView mCarrierLabel;
private boolean mCarrierLabelVisible = false;
private int mCarrierLabelHeight;
- private TextView mEmergencyCallLabel;
private int mStatusBarHeaderHeight;
private boolean mShowCarrierInPanel = false;
@@ -271,10 +278,6 @@
// the date view
DateView mDateView;
- // for heads up notifications
- private HeadsUpNotificationView mHeadsUpNotificationView;
- private int mHeadsUpNotificationDecay;
-
// on-screen navigation buttons
private NavigationBarView mNavigationBarView = null;
private int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
@@ -283,6 +286,7 @@
int mTrackingPosition; // the position of the top of the tracking view.
// ticker
+ private boolean mTickerEnabled;
private Ticker mTicker;
private View mTickerView;
private boolean mTicking;
@@ -359,7 +363,7 @@
if (!mUseHeadsUp) {
Log.d(TAG, "dismissing any existing heads up notification on disable event");
setHeadsUpVisibility(false);
- mHeadsUpNotificationView.setNotification(null);
+ mHeadsUpNotificationView.release();
removeHeadsUpView();
} else {
addHeadsUpView();
@@ -396,6 +400,9 @@
private boolean mSettingsClosing;
private boolean mVisible;
+ private Interpolator mAlphaOut = new PathInterpolator(0f, 0.4f, 1f, 1f);
+ private Interpolator mAlphaIn = new PathInterpolator(0f, 0f, 0.8f, 1f);
+
private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
new OnChildLocationsChangedListener() {
@Override
@@ -404,6 +411,8 @@
}
};
+ private int mDisabledUnmodified;
+
public void setOnFlipRunnable(Runnable onFlipRunnable) {
mOnFlipRunnable = onFlipRunnable;
}
@@ -633,7 +642,6 @@
mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
mNotificationIcons.setOverflowIndicator(mMoreIcon);
mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
- mTickerView = mStatusBarView.findViewById(R.id.ticker);
mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
R.id.notification_stack_scroller);
@@ -662,8 +670,9 @@
mKeyguardBottomArea =
(KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
mKeyguardBottomArea.setActivityStarter(this);
- mKeyguardIndicationTextView = (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
- R.id.keyguard_indication_text);
+ mKeyguardIndicationController = new KeyguardIndicationController(mContext,
+ (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
+ R.id.keyguard_indication_text));
mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
mDateTimeView = mHeader.findViewById(R.id.datetime);
@@ -672,14 +681,17 @@
mDateTimeView.setEnabled(true);
}
- mNotificationPanel.setSystemUiVisibility(
- View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
- View.STATUS_BAR_DISABLE_CLOCK);
+ mTickerEnabled = res.getBoolean(R.bool.enable_ticker);
+ if (mTickerEnabled) {
+ final ViewStub tickerStub = (ViewStub) mStatusBarView.findViewById(R.id.ticker_stub);
+ if (tickerStub != null) {
+ mTickerView = tickerStub.inflate();
+ mTicker = new MyTicker(context, mStatusBarView);
- mTicker = new MyTicker(context, mStatusBarView);
-
- TickerView tickerView = (TickerView)mStatusBarView.findViewById(R.id.tickerText);
- tickerView.mTicker = mTicker;
+ TickerView tickerView = (TickerView) mStatusBarView.findViewById(R.id.tickerText);
+ tickerView.mTicker = mTicker;
+ }
+ }
mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
@@ -704,23 +716,9 @@
mNetworkController.addSignalCluster(signalCluster);
signalCluster.setNetworkController(mNetworkController);
-
final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
if (isAPhone) {
- mEmergencyCallLabel =
- (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only);
- // TODO: Uncomment when correctly positioned
-// if (mEmergencyCallLabel != null) {
-// mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
-// mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
-// public void onClick(View v) { }});
-// mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-// @Override
-// public void onLayoutChange(View v, int left, int top, int right, int bottom,
-// int oldLeft, int oldTop, int oldRight, int oldBottom) {
-// updateCarrierLabelVisibility(false);
-// }});
-// }
+ mNetworkController.addEmergencyLabelView(mHeader);
}
mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
@@ -746,6 +744,8 @@
// updateCarrierLabelVisibility(false);
}
+ mBatteryController.setStatusBarHeaderView(mHeader);
+
// Set up the quick settings tile panel
mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
if (mQSPanel != null) {
@@ -753,7 +753,8 @@
@Override
public ValueAnimator createRevealAnimator(View v, int centerX, int centerY,
float startRadius, float endRadius) {
- return v.createRevealAnimator(centerX, centerY, startRadius, endRadius);
+ return ViewAnimationUtils.createCircularReveal(v, centerX, centerY,
+ startRadius, endRadius);
}
});
final QSTileHost qsh = new QSTileHost(mContext, this,
@@ -814,6 +815,10 @@
return mStatusBarView;
}
+ public StatusBarWindowView getStatusBarWindow() {
+ return mStatusBarWindow;
+ }
+
@Override
protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) {
boolean opaque = false;
@@ -1046,36 +1051,37 @@
}
@Override
- public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
- if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
- Entry shadeEntry = createNotificationViews(notification);
- if (shadeEntry == null) {
- return;
- }
+ public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
+ if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification, ranking)) {
// Forward the ranking so we can sort the new notification.
mNotificationData.updateRanking(ranking);
return;
}
+ mIntercepted.remove(notification.getKey());
+ displayNotification(notification, ranking);
+ }
+
+ public void displayNotification(StatusBarNotification notification, RankingMap ranking) {
if (mUseHeadsUp && shouldInterrupt(notification)) {
if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
Entry interruptionCandidate = new Entry(notification, null);
ViewGroup holder = mHeadsUpNotificationView.getHolder();
if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
- mInterruptingNotificationTime = System.currentTimeMillis();
- mInterruptingNotificationEntry = interruptionCandidate;
- shadeEntry.setInterruption();
-
// 1. Populate mHeadsUpNotificationView
- mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
+ mHeadsUpNotificationView.showNotification(interruptionCandidate);
- // 2. Animate mHeadsUpNotificationView in
- mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
-
- // 3. Set alarm to age the notification off
- resetHeadsUpDecayTimer();
+ // do not show the notification in the shade, yet.
+ return;
}
- } else if (notification.getNotification().fullScreenIntent != null) {
+ }
+
+ Entry shadeEntry = createNotificationViews(notification);
+ if (shadeEntry == null) {
+ return;
+ }
+
+ if (notification.getNotification().fullScreenIntent != null) {
// Stop screensaver if the notification has a full-screen intent.
// (like an incoming phone call)
awakenDreams();
@@ -1090,7 +1096,7 @@
// usual case: status bar visible & not immersive
// show the ticker if there isn't already a heads up
- if (mInterruptingNotificationEntry == null) {
+ if (mHeadsUpNotificationView.getEntry() == null) {
tick(notification, true);
}
}
@@ -1100,45 +1106,76 @@
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
}
+ public void displayNotificationFromHeadsUp(StatusBarNotification notification) {
+ NotificationData.Entry shadeEntry = createNotificationViews(notification);
+ if (shadeEntry == null) {
+ return;
+ }
+ shadeEntry.setInterruption();
+
+ addNotificationViews(shadeEntry, null);
+ // Recalculate the position of the sliding windows and the titles.
+ setAreThereNotifications();
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+
@Override
public void resetHeadsUpDecayTimer() {
- mHandler.removeMessages(MSG_HIDE_HEADS_UP);
+ mHandler.removeMessages(MSG_DECAY_HEADS_UP);
if (mUseHeadsUp && mHeadsUpNotificationDecay > 0
&& mHeadsUpNotificationView.isClearable()) {
- mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay);
+ mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay);
}
}
@Override
- public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
- super.updateNotificationInternal(notification, ranking);
- mIntercepted.update(notification);
+ public void scheduleHeadsUpOpen() {
+ mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
}
@Override
- protected void updateRankingInternal(Ranking ranking) {
+ public void scheduleHeadsUpClose() {
+ mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
+ }
+
+ @Override
+ public void scheduleHeadsUpEscalation() {
+ mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP);
+ }
+
+ @Override
+ public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
+ super.updateNotificationInternal(notification, ranking);
+ // if we're here, then the notification is already in the shade
+ mIntercepted.remove(notification.getKey());
+ }
+
+ @Override
+ protected void updateRankingInternal(RankingMap ranking) {
mNotificationData.updateRanking(ranking);
mIntercepted.retryIntercepts(ranking);
updateNotifications();
}
@Override
- public void removeNotificationInternal(String key, Ranking ranking) {
+ public void removeNotificationInternal(String key, RankingMap ranking) {
+ if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
+ && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
+ mHeadsUpNotificationView.clear();
+ }
+
StatusBarNotification old = removeNotificationViews(key, ranking);
if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
if (old != null) {
// Cancel the ticker if it's still running
- mTicker.removeEntry(old);
+ if (mTickerEnabled) {
+ mTicker.removeEntry(old);
+ }
// Recalculate the position of the sliding windows and the titles.
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null
- && old == mInterruptingNotificationEntry.notification) {
- mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
- }
-
if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0
&& !mNotificationPanel.isTracking() && mState != StatusBarState.KEYGUARD) {
animateCollapsePanels();
@@ -1174,15 +1211,26 @@
Entry ent = mNotificationData.get(i);
if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
- // TODO How do we want to badge notifcations from profiles.
if (!notificationIsForCurrentProfiles(ent.notification)) continue;
final int vis = ent.notification.getNotification().visibility;
if (vis != Notification.VISIBILITY_SECRET) {
// when isLockscreenPublicMode() we show the public form of VISIBILITY_PRIVATE notifications
- ent.row.setShowingPublic(isLockscreenPublicMode()
+ boolean showingPublic = isLockscreenPublicMode()
&& vis == Notification.VISIBILITY_PRIVATE
- && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId()));
+ && !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
+ ent.row.setShowingPublic(showingPublic);
+ if (ent.autoRedacted && ent.legacy) {
+ if (showingPublic) {
+ ent.row.setBackgroundResourceIds(
+ com.android.internal.R.drawable.notification_material_bg,
+ com.android.internal.R.drawable.notification_material_bg_dim);
+ } else {
+ ent.row.setBackgroundResourceIds(
+ com.android.internal.R.drawable.notification_bg,
+ com.android.internal.R.drawable.notification_bg_dim);
+ }
+ }
toShow.add(ent.row);
}
}
@@ -1326,7 +1374,8 @@
mCarrierLabelHeight));
}
- final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
+ // Emergency calls only is shown in the expanded header now.
+ final boolean emergencyCallsShownElsewhere = true;
final boolean makeVisible =
!(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
&& mStackScroller.getHeight() < (mNotificationPanel.getHeight()
@@ -1403,10 +1452,20 @@
}
}
+ private int adjustDisableFlags(int state) {
+ if (mExpandedVisible) {
+ state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
+ state |= StatusBarManager.DISABLE_SYSTEM_INFO;
+ }
+ return state;
+ }
+
/**
* State is one or more of the DISABLE constants from StatusBarManager.
*/
public void disable(int state) {
+ mDisabledUnmodified = state;
+ state = adjustDisableFlags(state);
final int old = mDisabled;
final int diff = state ^ old;
mDisabled = state;
@@ -1444,20 +1503,17 @@
if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
mSystemIconArea.animate()
.alpha(0f)
- .translationY(mNaturalBarHeight*0.5f)
- .setDuration(175)
- .setInterpolator(new DecelerateInterpolator(1.5f))
- .setListener(mMakeIconsInvisible)
- .start();
+ .withLayer()
+ .setDuration(160)
+ .setInterpolator(mAlphaIn)
+ .setListener(mMakeIconsInvisible);
} else {
mSystemIconArea.setVisibility(View.VISIBLE);
mSystemIconArea.animate()
.alpha(1f)
- .translationY(0)
- .setStartDelay(0)
- .setInterpolator(new DecelerateInterpolator(1.5f))
- .setDuration(175)
- .start();
+ .withLayer()
+ .setInterpolator(mAlphaOut)
+ .setDuration(320);
}
}
@@ -1493,20 +1549,18 @@
mNotificationIcons.animate()
.alpha(0f)
- .translationY(mNaturalBarHeight*0.5f)
- .setDuration(175)
- .setInterpolator(new DecelerateInterpolator(1.5f))
+ .withLayer()
+ .setDuration(160)
+ .setInterpolator(mAlphaIn)
.setListener(mMakeIconsInvisible)
.start();
} else {
mNotificationIcons.setVisibility(View.VISIBLE);
mNotificationIcons.animate()
.alpha(1f)
- .translationY(0)
- .setStartDelay(0)
- .setInterpolator(new DecelerateInterpolator(1.5f))
- .setDuration(175)
- .start();
+ .withLayer()
+ .setInterpolator(mAlphaOut)
+ .setDuration(320);
}
}
}
@@ -1544,7 +1598,12 @@
case MSG_SHOW_HEADS_UP:
setHeadsUpVisibility(true);
break;
+ case MSG_DECAY_HEADS_UP:
+ mHeadsUpNotificationView.release();
+ setHeadsUpVisibility(false);
+ break;
case MSG_HIDE_HEADS_UP:
+ mHeadsUpNotificationView.release();
setHeadsUpVisibility(false);
break;
case MSG_ESCALATE_HEADS_UP:
@@ -1557,8 +1616,9 @@
/** if the interrupting notification had a fullscreen intent, fire it now. */
private void escalateHeadsUp() {
- if (mInterruptingNotificationEntry != null) {
- final StatusBarNotification sbn = mInterruptingNotificationEntry.notification;
+ if (mHeadsUpNotificationView.getEntry() != null) {
+ final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification;
+ mHeadsUpNotificationView.release();
final Notification notification = sbn.getNotification();
if (notification.fullScreenIntent != null) {
if (DEBUG)
@@ -1606,7 +1666,7 @@
mStatusBarWindowManager.setStatusBarExpanded(true);
visibilityChanged(true);
-
+ disable(mDisabledUnmodified);
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
@@ -1646,7 +1706,6 @@
}
if (mStatusBarWindow != null) {
-
// release focus immediately to kick off focus change transition
mStatusBarWindowManager.setStatusBarFocusable(false);
@@ -1757,10 +1816,6 @@
mStatusBarView.collapseAllPanels(true);
}
- void makeExpandedInvisibleSoon() {
- mHandler.postDelayed(new Runnable() { public void run() { makeExpandedInvisible(); }}, 50);
- }
-
void makeExpandedInvisible() {
if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
+ " mExpandedVisible=" + mExpandedVisible);
@@ -1804,7 +1859,7 @@
}
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
-
+ disable(mDisabledUnmodified);
showBouncer();
}
@@ -1892,6 +1947,10 @@
Integer.toHexString(oldVal), Integer.toHexString(newVal),
Integer.toHexString(diff)));
if (diff != 0) {
+ // we never set the recents bit via this method, so save the prior state to prevent
+ // clobbering the bit below
+ final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
+
mSystemUiVisibility = newVal;
// update low profile
@@ -1946,6 +2005,11 @@
mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
}
+ // restore the recents bit
+ if (wasRecentsVisible) {
+ mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
+ }
+
// send updated sysui visibility to window manager
notifyUiVisibilityChanged(mSystemUiVisibility);
}
@@ -2102,6 +2166,8 @@
@Override
protected void tick(StatusBarNotification n, boolean firstTime) {
+ if (!mTickerEnabled) return;
+
// no ticking in lights-out mode
if (!areLightsOn()) return;
@@ -2118,7 +2184,7 @@
if (n.getNotification().tickerText != null && mStatusBarWindow != null
&& mStatusBarWindow.getWindowToken() != null) {
if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS
- | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+ | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
mTicker.addEntry(n);
}
}
@@ -2127,10 +2193,14 @@
private class MyTicker extends Ticker {
MyTicker(Context context, View sb) {
super(context, sb);
+ if (!mTickerEnabled) {
+ Log.w(TAG, "MyTicker instantiated with mTickerEnabled=false", new Throwable());
+ }
}
@Override
public void tickerStarting() {
+ if (!mTickerEnabled) return;
mTicking = true;
mStatusBarContents.setVisibility(View.GONE);
mTickerView.setVisibility(View.VISIBLE);
@@ -2140,6 +2210,7 @@
@Override
public void tickerDone() {
+ if (!mTickerEnabled) return;
mStatusBarContents.setVisibility(View.VISIBLE);
mTickerView.setVisibility(View.GONE);
mStatusBarContents.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
@@ -2148,6 +2219,7 @@
}
public void tickerHalting() {
+ if (!mTickerEnabled) return;
if (mStatusBarContents.getVisibility() != View.VISIBLE) {
mStatusBarContents.setVisibility(View.VISIBLE);
mStatusBarContents
@@ -2186,11 +2258,14 @@
pw.println("Current Status Bar state:");
pw.println(" mExpandedVisible=" + mExpandedVisible
+ ", mTrackingPosition=" + mTrackingPosition);
- pw.println(" mTicking=" + mTicking);
+ pw.println(" mTickerEnabled=" + mTickerEnabled);
+ if (mTickerEnabled) {
+ pw.println(" mTicking=" + mTicking);
+ pw.println(" mTickerView: " + viewInfo(mTickerView));
+ }
pw.println(" mTracking=" + mTracking);
pw.println(" mDisplayMetrics=" + mDisplayMetrics);
pw.println(" mStackScroller: " + viewInfo(mStackScroller));
- pw.println(" mTickerView: " + viewInfo(mTickerView));
pw.println(" mStackScroller: " + viewInfo(mStackScroller)
+ " scroll " + mStackScroller.getScrollX()
+ "," + mStackScroller.getScrollY());
@@ -2206,7 +2281,7 @@
pw.print(" mUseHeadsUp=");
pw.println(mUseHeadsUp);
pw.print(" interrupting package: ");
- pw.println(hunStateToString(mInterruptingNotificationEntry));
+ pw.println(hunStateToString(mHeadsUpNotificationView.getEntry()));
dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
if (mNavigationBarView != null) {
pw.print(" mNavigationBarWindowState=");
@@ -2480,26 +2555,10 @@
if (!ENABLE_HEADS_UP) return;
if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window");
mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE);
- if (!vis) {
- if (DEBUG) Log.d(TAG, "setting heads up entry to null");
- mInterruptingNotificationEntry = null;
- }
}
public void onHeadsUpDismissed() {
- if (mInterruptingNotificationEntry == null) return;
- mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
- if (mHeadsUpNotificationView.isClearable()) {
- try {
- mBarService.onNotificationClear(
- mInterruptingNotificationEntry.notification.getPackageName(),
- mInterruptingNotificationEntry.notification.getTag(),
- mInterruptingNotificationEntry.notification.getId(),
- mInterruptingNotificationEntry.notification.getUserId());
- } catch (android.os.RemoteException ex) {
- // oh well
- }
- }
+ mHeadsUpNotificationView.dismiss();
}
/**
@@ -2660,7 +2719,9 @@
@Override
protected void haltTicker() {
- mTicker.halt();
+ if (mTickerEnabled) {
+ mTicker.halt();
+ }
}
@Override
@@ -2840,19 +2901,20 @@
}
private void updatePublicMode() {
- setLockscreenPublicMode(mState == StatusBarState.KEYGUARD
+ setLockscreenPublicMode(
+ (mStatusBarKeyguardViewManager.isShowing() ||
+ mStatusBarKeyguardViewManager.isOccluded())
&& mStatusBarKeyguardViewManager.isSecure());
}
private void updateKeyguardState() {
if (mState == StatusBarState.KEYGUARD) {
mKeyguardStatusView.setVisibility(View.VISIBLE);
- mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
- mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+ mKeyguardIndicationController.setVisible(true);
mNotificationPanel.resetViews();
} else {
mKeyguardStatusView.setVisibility(View.GONE);
- mKeyguardIndicationTextView.setVisibility(View.GONE);
+ mKeyguardIndicationController.setVisible(false);
}
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
mKeyguardBottomArea.setVisibility(View.VISIBLE);
@@ -2865,7 +2927,6 @@
mNotificationPanel.setKeyguardShowing(false);
mScrimController.setKeyguardShowing(false);
}
-
updateStackScrollerState();
updatePublicMode();
updateNotifications();
@@ -2875,9 +2936,17 @@
public void updateStackScrollerState() {
if (mStackScroller == null) return;
- mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
- mStackScroller.setVisibility(!mShowLockscreenNotifications && mState == StatusBarState.KEYGUARD
+ boolean onKeyguard = mState == StatusBarState.KEYGUARD;
+ mStackScroller.setDimmed(onKeyguard, false /* animate */);
+ mStackScroller.setVisibility(!mShowLockscreenNotifications && onKeyguard
? View.INVISIBLE : View.VISIBLE);
+ mStackScroller.setScrollingEnabled(!onKeyguard);
+ mStackScroller.setExpandingEnabled(!onKeyguard);
+ ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
+ mStackScroller.setActivatedChild(null);
+ if (activatedChild != null) {
+ activatedChild.makeInactive(false /* animate */);
+ }
}
public void userActivity() {
@@ -2911,22 +2980,9 @@
private void instantExpandNotificationsPanel() {
- // Make our window larger and the panel visible.
+ // Make our window larger and the panel expanded.
makeExpandedVisible(true);
- mNotificationPanel.setVisibility(View.VISIBLE);
-
- // Wait for window manager to pickup the change, so we know the maximum height of the panel
- // then.
- mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- if (mStatusBarWindow.getHeight() != getStatusBarHeight()) {
- mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- mNotificationPanel.setExpandedFraction(1);
- }
- }
- });
+ mNotificationPanel.instantExpand();
}
private void instantCollapseNotificationPanel() {
@@ -2934,9 +2990,13 @@
}
@Override
- public void onActivated(View view) {
+ public void onActivated(ActivatableNotificationView view) {
userActivity();
- mKeyguardIndicationTextView.switchIndication(R.string.notification_tap_again);
+ mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again);
+ ActivatableNotificationView previousView = mStackScroller.getActivatedChild();
+ if (previousView != null) {
+ previousView.makeInactive(true /* animate */);
+ }
mStackScroller.setActivatedChild(view);
}
@@ -2949,9 +3009,9 @@
}
@Override
- public void onActivationReset(View view) {
+ public void onActivationReset(ActivatableNotificationView view) {
if (view == mStackScroller.getActivatedChild()) {
- mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+ mKeyguardIndicationController.hideTransientIndication();
mStackScroller.setActivatedChild(null);
}
}
@@ -2960,11 +3020,20 @@
}
public void onUnlockHintStarted() {
- mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
+ mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock);
}
- public void onUnlockHintFinished() {
- mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+ public void onHintFinished() {
+ // Delay the reset a bit so the user can read the text.
+ mKeyguardIndicationController.hideTransientIndicationDelayed(HINT_RESET_DELAY_MS);
+ }
+
+ public void onCameraHintStarted() {
+ mKeyguardIndicationController.showTransientIndication(R.string.camera_hint);
+ }
+
+ public void onPhoneHintStarted() {
+ mKeyguardIndicationController.showTransientIndication(R.string.phone_hint);
}
public void onTrackingStopped(boolean expand) {
@@ -3000,6 +3069,11 @@
mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
}
+ @Override
+ public void onTouchSlopExceeded() {
+ mStackScroller.removeLongPressCallback();
+ }
+
/**
* If secure with redaction: Show bouncer, go to unlocked shade.
*
@@ -3023,6 +3097,16 @@
}
/**
+ * Goes back to the keyguard after hanging around in {@link StatusBarState#SHADE_LOCKED}.
+ */
+ public void goToKeyguard() {
+ if (mState == StatusBarState.SHADE_LOCKED) {
+ setBarState(StatusBarState.KEYGUARD);
+ updateKeyguardState();
+ }
+ }
+
+ /**
* @return a ViewGroup that spans the entire panel which contains the quick settings
*/
public ViewGroup getQuickSettingsOverlayParent() {
@@ -3056,4 +3140,41 @@
}
}
};
+
+ // Recents
+
+ @Override
+ protected void showRecents(boolean triggeredFromAltTab) {
+ // Set the recents visibility flag
+ mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.showRecents(triggeredFromAltTab);
+ }
+
+ @Override
+ protected void hideRecents(boolean triggeredFromAltTab) {
+ // Unset the recents visibility flag
+ mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.hideRecents(triggeredFromAltTab);
+ }
+
+ @Override
+ protected void toggleRecents() {
+ // Toggle the recents visibility flag
+ mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.toggleRecents();
+ }
+
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ // Update the recents visibility flag
+ if (visible) {
+ mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
+ } else {
+ mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
+ }
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 910d88c..3a17177 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -111,8 +111,7 @@
@Override
public void onAllPanelsCollapsed() {
super.onAllPanelsCollapsed();
- // give animations time to settle
- mBar.makeExpandedInvisibleSoon();
+ mBar.makeExpandedInvisible();
mLastFullyOpenedPanel = null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 13d3291..c8ab027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -26,6 +26,7 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
@@ -57,6 +58,11 @@
private View mSignalCluster;
private View mSettingsButton;
private View mBrightnessContainer;
+ private View mEmergencyCallsOnly;
+ private TextView mChargingInfo;
+
+ private boolean mShowEmergencyCallsOnly;
+ private boolean mShowChargingInfo;
private int mCollapsedHeight;
private int mExpandedHeight;
@@ -91,6 +97,8 @@
mBrightnessController = new BrightnessController(getContext(),
(ImageView) findViewById(R.id.brightness_icon),
(ToggleSlider) findViewById(R.id.brightness_slider));
+ mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
+ mChargingInfo = (TextView) findViewById(R.id.header_charging_info);
loadDimens();
updateVisibilities();
addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@@ -138,6 +146,7 @@
updateBrightnessControllerState();
updateZTranslation();
updateClickTargets();
+ updateWidth();
if (mQSPanel != null) {
mQSPanel.setExpanded(expanded && !overscrolled);
}
@@ -173,7 +182,7 @@
}
private void updateWidth() {
- int width = mKeyguardShowing ? mKeyguardWidth : mNormalWidth;
+ int width = (mKeyguardShowing && !mExpanded) ? mKeyguardWidth : mNormalWidth;
ViewGroup.LayoutParams lp = getLayoutParams();
if (width != lp.width) {
lp.width = width;
@@ -195,13 +204,32 @@
if (mSignalCluster != null) {
mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
}
+ mEmergencyCallsOnly.setVisibility(mExpanded && !mOverscrolled && mShowEmergencyCallsOnly
+ ? VISIBLE : GONE);
+ mChargingInfo.setVisibility(mExpanded && !mOverscrolled && mShowChargingInfo
+ && !mShowEmergencyCallsOnly ? VISIBLE : GONE);
}
private void updateSystemIconsLayoutParams() {
RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams();
- lp.addRule(RelativeLayout.START_OF, mExpanded
- ? mSettingsButton.getId()
- : mMultiUserSwitch.getId());
+ boolean systemIconsAboveClock = mExpanded && !mOverscrolled
+ && mShowChargingInfo && !mShowEmergencyCallsOnly;
+ if (systemIconsAboveClock) {
+ lp.addRule(ALIGN_PARENT_START);
+ lp.removeRule(START_OF);
+ } else {
+ lp.addRule(RelativeLayout.START_OF, mExpanded
+ ? mSettingsButton.getId()
+ : mMultiUserSwitch.getId());
+ lp.removeRule(ALIGN_PARENT_START);
+ }
+
+ RelativeLayout.LayoutParams clockLp = (LayoutParams) mDateTime.getLayoutParams();
+ if (systemIconsAboveClock) {
+ clockLp.addRule(BELOW, mChargingInfo.getId());
+ } else {
+ clockLp.addRule(BELOW, mEmergencyCallsOnly.getId());
+ }
}
private void updateBrightnessControllerState() {
@@ -307,4 +335,29 @@
mBrightnessContainer.animate().alpha(showingDetail ? 0 : 1).withLayer().start();
}
};
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
+ public void setShowEmergencyCallsOnly(boolean show) {
+ mShowEmergencyCallsOnly = show;
+ if (mExpanded) {
+ updateVisibilities();
+ updateSystemIconsLayoutParams();
+ }
+ }
+
+ public void setShowChargingInfo(boolean showChargingInfo) {
+ mShowChargingInfo = showChargingInfo;
+ if (mExpanded) {
+ updateVisibilities();
+ updateSystemIconsLayoutParams();
+ }
+ }
+
+ public void setChargingInfo(CharSequence chargingInfo) {
+ mChargingInfo.setText(chargingInfo);
+ }
}
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 e3145a6..09e4d94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -181,6 +181,10 @@
reset();
}
+ public boolean isOccluded() {
+ return mOccluded;
+ }
+
/**
* Hides the keyguard view
*/
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 b51626d..d5e8e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -40,7 +40,6 @@
public static final String TAG = "StatusBarWindowView";
public static final boolean DEBUG = BaseStatusBar.DEBUG;
- private ExpandHelper mExpandHelper;
private DragDownHelper mDragDownHelper;
private NotificationStackScrollLayout mStackScrollLayout;
private NotificationPanelView mNotificationPanel;
@@ -73,12 +72,6 @@
mStackScrollLayout = (NotificationStackScrollLayout) findViewById(
R.id.notification_stack_scroller);
mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel);
- int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
- int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
- mExpandHelper = new ExpandHelper(getContext(), mStackScrollLayout,
- minHeight, maxHeight);
- mExpandHelper.setEventSource(this);
- mExpandHelper.setScrollAdapter(mStackScrollLayout);
mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
// We really need to be able to animate while window animations are going on
@@ -114,12 +107,6 @@
boolean intercept = false;
if (mNotificationPanel.isFullyExpanded()
&& mStackScrollLayout.getVisibility() == View.VISIBLE
- && (mService.getBarState() == StatusBarState.SHADE
- || (mService.getBarState() == StatusBarState.SHADE_LOCKED
- && !mService.isBouncerShowing()))) {
- intercept = mExpandHelper.onInterceptTouchEvent(ev);
- } else if (mNotificationPanel.isFullyExpanded()
- && mStackScrollLayout.getVisibility() == View.VISIBLE
&& mService.getBarState() == StatusBarState.KEYGUARD
&& !mService.isBouncerShowing()) {
intercept = mDragDownHelper.onInterceptTouchEvent(ev);
@@ -139,10 +126,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
- if (mNotificationPanel.isFullyExpanded()
- && mService.getBarState() != StatusBarState.KEYGUARD) {
- handled = mExpandHelper.onTouchEvent(ev);
- } else if (mService.getBarState() == StatusBarState.KEYGUARD) {
+ if (mService.getBarState() == StatusBarState.KEYGUARD) {
handled = mDragDownHelper.onTouchEvent(ev);
}
if (!handled) {
@@ -168,8 +152,8 @@
}
public void cancelExpandHelper() {
- if (mExpandHelper != null) {
- mExpandHelper.cancel();
+ if (mStackScrollLayout != null) {
+ mStackScrollLayout.cancelExpandHelper();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java
index 8aa3837..bf13751 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TickerView.java
@@ -32,7 +32,7 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- mTicker.reflowText();
+ if (mTicker != null) mTicker.reflowText();
}
public void setTicker(Ticker t) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 6db9bc3..4cf66a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,26 +16,49 @@
package com.android.systemui.statusbar.policy;
+import com.android.internal.app.IBatteryStats;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarHeaderView;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.format.Formatter;
+import android.util.Log;
import java.util.ArrayList;
public class BatteryController extends BroadcastReceiver {
private static final String TAG = "StatusBar.BatteryController";
-
private ArrayList<BatteryStateChangeCallback> mChangeCallbacks =
new ArrayList<BatteryStateChangeCallback>();
+ private Context mContext;
+ private StatusBarHeaderView mStatusBarHeaderView;
+ private IBatteryStats mBatteryInfo;
+
+ private int mLevel;
+ private boolean mPluggedIn;
+ private boolean mCharging;
+ private boolean mCharged;
+
+
public interface BatteryStateChangeCallback {
- public void onBatteryLevelChanged(int level, boolean pluggedIn);
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
}
public BatteryController(Context context) {
+ mContext = context;
+
+ mBatteryInfo = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(this, filter);
@@ -45,24 +68,59 @@
mChangeCallbacks.add(cb);
}
+ public void setStatusBarHeaderView(StatusBarHeaderView statusBarHeaderView) {
+ mStatusBarHeaderView = statusBarHeaderView;
+ updateStatusBarHeaderView();
+ }
+
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ mLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+
final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
+ mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
+ mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
- boolean plugged = false;
- switch (status) {
- case BatteryManager.BATTERY_STATUS_CHARGING:
- case BatteryManager.BATTERY_STATUS_FULL:
- plugged = true;
- break;
- }
-
+ updateStatusBarHeaderView();
for (BatteryStateChangeCallback cb : mChangeCallbacks) {
- cb.onBatteryLevelChanged(level, plugged);
+ cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
}
}
}
+
+ private void updateStatusBarHeaderView() {
+ if (mStatusBarHeaderView != null) {
+ mStatusBarHeaderView.setShowChargingInfo(mPluggedIn);
+ mStatusBarHeaderView.setChargingInfo(computeChargingInfo());
+ }
+ }
+
+ private String computeChargingInfo() {
+ if (!mPluggedIn || !mCharged && !mCharging) {
+ return mContext.getResources().getString(R.string.expanded_header_battery_not_charging);
+ }
+
+ if (mCharged) {
+ return mContext.getResources().getString(R.string.expanded_header_battery_charged);
+ }
+
+ // Try fetching charging time from battery stats.
+ try {
+ long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
+ if (chargingTimeRemaining > 0) {
+ String chargingTimeFormatted =
+ Formatter.formatShortElapsedTime(mContext, chargingTimeRemaining);
+ return mContext.getResources().getString(
+ R.string.expanded_header_battery_charging_with_time, chargingTimeFormatted);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IBatteryStats: ", e);
+ }
+
+ // Fall back to simple charging label.
+ return mContext.getResources().getString(R.string.expanded_header_battery_charging);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 117bf61..379b509 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -45,6 +45,7 @@
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+ filter.addAction(BluetoothDevice.ACTION_ALIAS_CHANGED);
context.registerReceiver(this, filter);
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -104,8 +105,8 @@
@Override
public String getLastDeviceName() {
- return mLastDevice != null ? mLastDevice.getName()
- : mBondedDevices.size() == 1 ? mBondedDevices.iterator().next().getName()
+ return mLastDevice != null ? mLastDevice.getAliasName()
+ : mBondedDevices.size() == 1 ? mBondedDevices.iterator().next().getAliasName()
: null;
}
@@ -122,6 +123,9 @@
== BluetoothAdapter.STATE_CONNECTING;
mLastDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
}
+ if (action.equals(BluetoothDevice.ACTION_ALIAS_CHANGED)) {
+ mLastDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ }
fireCallbacks();
updateBondedBluetoothDevices();
}
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 ac26da2..0a48e34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -33,9 +33,9 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback,
ViewTreeObserver.OnComputeInternalInsetsListener {
@@ -51,7 +51,7 @@
private SwipeHelper mSwipeHelper;
private EdgeSwipeHelper mEdgeSwipeHelper;
- private BaseStatusBar mBar;
+ private PhoneStatusBar mBar;
private ExpandHelper mExpandHelper;
private long mStartTouchTime;
@@ -69,7 +69,7 @@
if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
}
- public void setBar(BaseStatusBar bar) {
+ public void setBar(PhoneStatusBar bar) {
mBar = bar;
}
@@ -77,7 +77,12 @@
return mContentHolder;
}
- public boolean setNotification(NotificationData.Entry headsUp) {
+ public boolean showNotification(NotificationData.Entry headsUp) {
+ if (mHeadsUp != null && headsUp != null && !mHeadsUp.key.equals(headsUp.key)) {
+ // bump any previous heads up back to the shade
+ release();
+ }
+
mHeadsUp = headsUp;
if (mContentHolder != null) {
mContentHolder.removeAllViews();
@@ -97,10 +102,57 @@
mSwipeHelper.snapChild(mContentHolder, 1f);
mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay;
+
+ mHeadsUp.setInterruption();
+
+ // 2. Animate mHeadsUpNotificationView in
+ mBar.scheduleHeadsUpOpen();
+
+ // 3. Set alarm to age the notification off
+ mBar.resetHeadsUpDecayTimer();
}
return true;
}
+ public boolean isShowing(String key) {
+ return mHeadsUp != null && mHeadsUp.key.equals(key);
+ }
+
+ /** Discard the Heads Up notification. */
+ public void clear() {
+ mHeadsUp = null;
+ mBar.scheduleHeadsUpClose();
+ }
+
+ /** Respond to dismissal of the Heads Up window. */
+ public void dismiss() {
+ if (mHeadsUp == null) return;
+ if (mHeadsUp.notification.isClearable()) {
+ mBar.onNotificationClear(mHeadsUp.notification);
+ } else {
+ release();
+ }
+ mHeadsUp = null;
+ mBar.scheduleHeadsUpClose();
+ }
+
+ /** Push any current Heads Up notification down into the shade. */
+ public void release() {
+ if (mHeadsUp != null) {
+ mBar.displayNotificationFromHeadsUp(mHeadsUp.notification);
+ }
+ mHeadsUp = null;
+ }
+
+ public void releaseAndClose() {
+ release();
+ mBar.scheduleHeadsUpClose();
+ }
+
+ public NotificationData.Entry getEntry() {
+ return mHeadsUp;
+ }
+
public boolean isClearable() {
return mHeadsUp == null || mHeadsUp.notification.isClearable();
}
@@ -125,7 +177,7 @@
if (mHeadsUp != null) {
// whoops, we're on already!
- setNotification(mHeadsUp);
+ showNotification(mHeadsUp);
}
getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -225,6 +277,11 @@
}
}
+ @Override
+ public void expansionStateChanged(boolean isExpanding) {
+
+ }
+
// SwipeHelper.Callback methods
@Override
@@ -277,6 +334,10 @@
mTmpTwoArray[1] + mContentHolder.getHeight());
}
+ public void escalate() {
+ mBar.scheduleHeadsUpEscalation();
+ }
+
private class EdgeSwipeHelper implements Gefingerpoken {
private static final boolean DEBUG_EDGE_SWIPE = false;
private final float mTouchSlop;
@@ -330,4 +391,4 @@
return mConsuming;
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index dc8f315..1f68860 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -30,7 +30,7 @@
void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId,
String mobileSignalContentDescriptionId, int dataTypeIconId,
boolean activityIn, boolean activityOut,
- String dataTypeContentDescriptionId, String description);
+ String dataTypeContentDescriptionId, String description, boolean noSim);
void onAirplaneModeChanged(boolean enabled);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 56402a5..4e54e41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -47,6 +47,7 @@
import com.android.internal.util.AsyncChannel;
import com.android.systemui.DemoMode;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarHeaderView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -87,6 +88,7 @@
int mQSDataTypeIconId;
int mAirplaneIconId;
boolean mDataActive;
+ boolean mNoSim;
int mLastSignalLevel;
boolean mShowPhoneRSSIForData = false;
boolean mShowAtLeastThreeGees = false;
@@ -143,7 +145,7 @@
ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>();
ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>();
ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>();
- ArrayList<TextView> mEmergencyLabelViews = new ArrayList<TextView>();
+ ArrayList<StatusBarHeaderView> mEmergencyViews = new ArrayList<>();
ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
new ArrayList<NetworkSignalChangedCallback>();
@@ -261,8 +263,8 @@
mWifiLabelViews.add(v);
}
- public void addEmergencyLabelView(TextView v) {
- mEmergencyLabelViews.add(v);
+ public void addEmergencyLabelView(StatusBarHeaderView v) {
+ mEmergencyViews.add(v);
}
public void addSignalCluster(SignalCluster cluster) {
@@ -349,18 +351,18 @@
if (isEmergencyOnly()) {
cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId,
mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
- mContentDescriptionDataType, null);
+ mContentDescriptionDataType, null, mNoSim);
} else {
if (mIsWimaxEnabled && mWimaxConnected) {
// Wimax is special
cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId,
mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
- mContentDescriptionDataType, mNetworkName);
+ mContentDescriptionDataType, mNetworkName, mNoSim);
} else {
// Normal mobile data
cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId,
mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut,
- mContentDescriptionDataType, mNetworkName);
+ mContentDescriptionDataType, mNetworkName, mNoSim);
}
}
cb.onAirplaneModeChanged(mAirplaneMode);
@@ -736,6 +738,7 @@
// GSM case, we have to check also the sim state
if (mSimState == IccCardConstants.State.READY ||
mSimState == IccCardConstants.State.UNKNOWN) {
+ mNoSim = false;
if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
switch (mDataActivity) {
case TelephonyManager.DATA_ACTIVITY_IN:
@@ -758,6 +761,7 @@
}
} else {
iconId = R.drawable.stat_sys_no_sim;
+ mNoSim = true;
visible = false; // no SIM? no data
}
} else {
@@ -1251,15 +1255,10 @@
}
// e-call label
- N = mEmergencyLabelViews.size();
+ N = mEmergencyViews.size();
for (int i=0; i<N; i++) {
- TextView v = mEmergencyLabelViews.get(i);
- if (!emergencyOnly) {
- v.setVisibility(View.GONE);
- } else {
- v.setText(mobileLabel); // comes from the telephony stack
- v.setVisibility(View.VISIBLE);
- }
+ StatusBarHeaderView v = mEmergencyViews.get(i);
+ v.setShowEmergencyCallsOnly(emergencyOnly);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 6225c12..4cf72a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
+import android.net.Uri;
import android.service.notification.Condition;
public interface ZenModeController {
@@ -24,10 +25,12 @@
void setZen(boolean zen);
boolean isZen();
void requestConditions(boolean request);
- void select(Condition condition);
+ void setExitConditionId(Uri exitConditionId);
+ Uri getExitConditionId();
public static class Callback {
public void onZenChanged(boolean zen) {}
+ public void onExitConditionChanged(Uri exitConditionId) {}
public void onConditionsChanged(Condition[] conditions) {}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index adf2935..da8fd32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -25,6 +25,7 @@
import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.IConditionListener;
+import android.service.notification.ZenModeConfig;
import android.util.Slog;
import com.android.systemui.qs.GlobalSetting;
@@ -35,10 +36,12 @@
/** Platform implementation of the zen mode controller. **/
public class ZenModeControllerImpl implements ZenModeController {
private static final String TAG = "ZenModeControllerImpl";
+ private static final boolean DEBUG = false;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final Context mContext;
- private final GlobalSetting mSetting;
+ private final GlobalSetting mModeSetting;
+ private final GlobalSetting mConfigSetting;
private final INotificationManager mNoMan;
private final LinkedHashMap<Uri, Condition> mConditions = new LinkedHashMap<Uri, Condition>();
@@ -46,13 +49,20 @@
public ZenModeControllerImpl(Context context, Handler handler) {
mContext = context;
- mSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
+ mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) {
@Override
protected void handleValueChanged(int value) {
fireZenChanged(value != 0);
}
};
- mSetting.setListening(true);
+ mConfigSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE_CONFIG_ETAG) {
+ @Override
+ protected void handleValueChanged(int value) {
+ fireExitConditionChanged();
+ }
+ };
+ mModeSetting.setListening(true);
+ mConfigSetting.setListening(true);
mNoMan = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
}
@@ -69,12 +79,12 @@
@Override
public boolean isZen() {
- return mSetting.getValue() != 0;
+ return mModeSetting.getValue() != 0;
}
@Override
public void setZen(boolean zen) {
- mSetting.setValue(zen ? 1 : 0);
+ mModeSetting.setValue(zen ? 1 : 0);
}
@Override
@@ -91,14 +101,27 @@
}
@Override
- public void select(Condition condition) {
+ public void setExitConditionId(Uri exitConditionId) {
try {
- mNoMan.setZenModeCondition(condition == null ? null : condition.id);
+ mNoMan.setZenModeCondition(exitConditionId);
} catch (RemoteException e) {
// noop
}
}
+ @Override
+ public Uri getExitConditionId() {
+ try {
+ final ZenModeConfig config = mNoMan.getZenModeConfig();
+ if (config != null) {
+ return config.exitConditionId;
+ }
+ } catch (RemoteException e) {
+ // noop
+ }
+ return null;
+ }
+
private void fireZenChanged(boolean zen) {
for (Callback cb : mCallbacks) {
cb.onZenChanged(zen);
@@ -111,6 +134,14 @@
}
}
+ private void fireExitConditionChanged() {
+ final Uri exitConditionId = getExitConditionId();
+ if (DEBUG) Slog.d(TAG, "exitConditionId changed: " + exitConditionId);
+ for (Callback cb : mCallbacks) {
+ cb.onExitConditionChanged(exitConditionId);
+ }
+ }
+
private void updateConditions(Condition[] conditions) {
if (conditions == null || conditions.length == 0) return;
for (Condition c : conditions) {
@@ -124,8 +155,8 @@
private final IConditionListener mListener = new IConditionListener.Stub() {
@Override
public void onConditionsReceived(Condition[] conditions) {
- Slog.d(TAG, "onConditionsReceived " + (conditions == null ? 0 : conditions.length)
- + " mRequesting=" + mRequesting);
+ if (DEBUG) Slog.d(TAG, "onConditionsReceived "
+ + (conditions == null ? 0 : conditions.length) + " mRequesting=" + mRequesting);
if (!mRequesting) return;
updateConditions(conditions);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index b21e12c..6d92b05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.stack;
import android.view.View;
+import com.android.systemui.statusbar.ActivatableNotificationView;
import java.util.ArrayList;
@@ -27,7 +28,7 @@
private ArrayList<View> mDraggedViews = new ArrayList<View>();
private int mScrollY;
private boolean mDimmed;
- private View mActivatedChild;
+ private ActivatableNotificationView mActivatedChild;
private float mOverScrollTopAmount;
private float mOverScrollBottomAmount;
private int mSpeedBumpIndex = -1;
@@ -64,7 +65,7 @@
* In dimmed mode, a child can be activated, which happens on the first tap of the double-tap
* interaction. This child is then scaled normally and its background is fully opaque.
*/
- public void setActivatedChild(View activatedChild) {
+ public void setActivatedChild(ActivatableNotificationView activatedChild) {
mActivatedChild = activatedChild;
}
@@ -72,7 +73,7 @@
return mDimmed;
}
- public View getActivatedChild() {
+ public ActivatableNotificationView getActivatedChild() {
return mActivatedChild;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 6892b85..94472a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -30,10 +30,10 @@
import android.view.ViewTreeObserver;
import android.view.animation.AnimationUtils;
import android.widget.OverScroller;
-
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
+import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.SpeedBumpView;
@@ -51,13 +51,16 @@
private static final String TAG = "NotificationStackScrollLayout";
private static final boolean DEBUG = false;
- private static final float RUBBER_BAND_FACTOR = 0.35f;
+ private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
+ private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
+ private static final float RUBBER_BAND_FACTOR_ON_PANEL_EXPAND = 0.21f;
/**
* Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
*/
private static final int INVALID_POINTER = -1;
+ private ExpandHelper mExpandHelper;
private SwipeHelper mSwipeHelper;
private boolean mSwipingInProgress;
private int mCurrentStackHeight = Integer.MAX_VALUE;
@@ -73,6 +76,7 @@
private float mMaxOverScroll;
private boolean mIsBeingDragged;
private int mLastMotionY;
+ private int mDownX;
private int mActivePointerId;
private int mSidePaddings;
@@ -106,6 +110,7 @@
private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
private boolean mAnimationsEnabled;
+ private boolean mChangePositionInProgress;
/**
* The raw amount of the overScroll on the top, which is not rubber-banded.
@@ -128,6 +133,39 @@
private boolean mChildrenUpdateRequested;
private SpeedBumpView mSpeedBumpView;
private boolean mIsExpansionChanging;
+ private boolean mExpandingNotification;
+ private boolean mExpandedInThisMotion;
+ private boolean mScrollingEnabled;
+
+ /**
+ * Was the scroller scrolled to the top when the down motion was observed?
+ */
+ private boolean mScrolledToTopOnFirstDown;
+
+ /**
+ * The minimal amount of over scroll which is needed in order to switch to the quick settings
+ * when over scrolling on a expanded card.
+ */
+ private float mMinTopOverScrollToEscape;
+ private int mIntrinsicPadding;
+ private int mNotificationTopPadding;
+ private int mMinStackHeight;
+ private boolean mDontReportNextOverScroll;
+
+ /**
+ * The maximum scrollPosition which we are allowed to reach when a notification was expanded.
+ * This is needed to avoid scrolling too far after the notification was collapsed in the same
+ * motion.
+ */
+ private int mMaxScrollAfterExpand;
+ private OnLongClickListener mLongClickListener;
+
+ /**
+ * Should in this touch motion only be scrolling allowed? It's true when the scroller was
+ * animating.
+ */
+ private boolean mOnlyScrollingInThisMotion;
+ private boolean mTouchEnabled = true;
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
= new ViewTreeObserver.OnPreDrawListener() {
@Override
@@ -194,6 +232,7 @@
float densityScale = getResources().getDisplayMetrics().density;
float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
+ mSwipeHelper.setLongPressListener(mLongClickListener);
mSidePaddings = context.getResources()
.getDimensionPixelSize(R.dimen.notification_side_padding);
@@ -207,6 +246,17 @@
mPaddingBetweenElementsNormal = context.getResources()
.getDimensionPixelSize(R.dimen.notification_padding);
updatePadding(false);
+ int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
+ int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_max_height);
+ mExpandHelper = new ExpandHelper(getContext(), this,
+ minHeight, maxHeight);
+ mExpandHelper.setEventSource(this);
+ mExpandHelper.setScrollAdapter(this);
+ mMinTopOverScrollToEscape = getResources().getDimensionPixelSize(
+ R.dimen.min_top_overscroll_to_qs);
+ mNotificationTopPadding = getResources().getDimensionPixelSize(
+ R.dimen.notifications_top_padding);
+ mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
}
private void updatePadding(boolean dimmed) {
@@ -215,6 +265,13 @@
: mPaddingBetweenElementsNormal;
mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
updateContentHeight();
+ notifyHeightChangeListener(null);
+ }
+
+ private void notifyHeightChangeListener(ExpandableView view) {
+ if (mOnHeightChangedListener != null) {
+ mOnHeightChangedListener.onHeightChanged(view);
+ }
}
@Override
@@ -343,7 +400,7 @@
return mTopPadding;
}
- public void setTopPadding(int topPadding, boolean animate) {
+ private void setTopPadding(int topPadding, boolean animate) {
if (mTopPadding != topPadding) {
mTopPadding = topPadding;
updateAlgorithmHeightAndPadding();
@@ -353,9 +410,7 @@
mNeedsAnimation = true;
}
requestChildrenUpdate();
- if (mOnHeightChangedListener != null) {
- mOnHeightChangedListener.onHeightChanged(null);
- }
+ notifyHeightChangeListener(null);
}
}
@@ -414,6 +469,7 @@
public void setLongPressListener(View.OnLongClickListener listener) {
mSwipeHelper.setLongPressListener(listener);
+ mLongClickListener = listener;
}
public void onChildDismissed(View v) {
@@ -511,6 +567,29 @@
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setUserLocked(userLocked);
}
+ removeLongPressCallback();
+ requestDisallowInterceptTouchEvent(true);
+ }
+
+ @Override
+ public void expansionStateChanged(boolean isExpanding) {
+ mExpandingNotification = isExpanding;
+ if (!mExpandedInThisMotion) {
+ mMaxScrollAfterExpand = mOwnScrollY;
+ mExpandedInThisMotion = true;
+ }
+ }
+
+ public void setScrollingEnabled(boolean enable) {
+ mScrollingEnabled = enable;
+ }
+
+ public void setExpandingEnabled(boolean enable) {
+ mExpandHelper.setEnabled(enable);
+ }
+
+ private boolean isScrollingEnabled() {
+ return mScrollingEnabled;
}
public View getChildContentView(View v) {
@@ -548,18 +627,44 @@
if (!isEnabled()) {
return false;
}
+ boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+ || ev.getActionMasked()== MotionEvent.ACTION_UP;
+ boolean expandWantsIt = false;
+ if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
+ if (isCancelOrUp) {
+ mExpandHelper.onlyObserveMovements(false);
+ }
+ boolean wasExpandingBefore = mExpandingNotification;
+ expandWantsIt = mExpandHelper.onTouchEvent(ev);
+ if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore) {
+ dispatchDownEventToScroller(ev);
+ }
+ }
boolean scrollerWantsIt = false;
- if (!mSwipingInProgress) {
+ if (!mSwipingInProgress && !mExpandingNotification) {
scrollerWantsIt = onScrollTouch(ev);
}
boolean horizontalSwipeWantsIt = false;
- if (!mIsBeingDragged) {
+ if (!mIsBeingDragged
+ && !mExpandingNotification
+ && !mExpandedInThisMotion
+ && !mOnlyScrollingInThisMotion) {
horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
}
- return horizontalSwipeWantsIt || scrollerWantsIt || super.onTouchEvent(ev);
+ return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+ }
+
+ private void dispatchDownEventToScroller(MotionEvent ev) {
+ MotionEvent downEvent = MotionEvent.obtain(ev);
+ downEvent.setAction(MotionEvent.ACTION_DOWN);
+ onScrollTouch(downEvent);
+ downEvent.recycle();
}
private boolean onScrollTouch(MotionEvent ev) {
+ if (!isScrollingEnabled()) {
+ return false;
+ }
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
@@ -583,6 +688,7 @@
// Remember where the motion event started
mLastMotionY = (int) ev.getY();
+ mDownX = (int) ev.getX();
mActivePointerId = ev.getPointerId(0);
break;
}
@@ -594,8 +700,11 @@
}
final int y = (int) ev.getY(activePointerIndex);
+ final int x = (int) ev.getX(activePointerIndex);
int deltaY = mLastMotionY - y;
- if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
+ final int xDiff = Math.abs(x - mDownX);
+ final int yDiff = Math.abs(deltaY);
+ if (!mIsBeingDragged && yDiff > mTouchSlop && yDiff > xDiff) {
setIsBeingDragged(true);
if (deltaY > 0) {
deltaY -= mTouchSlop;
@@ -606,7 +715,10 @@
if (mIsBeingDragged) {
// Scroll to follow the motion event
mLastMotionY = y;
- final int range = getScrollRange();
+ int range = getScrollRange();
+ if (mExpandedInThisMotion) {
+ range = Math.min(range, mMaxScrollAfterExpand);
+ }
float scrollAmount;
if (deltaY < 0) {
@@ -631,19 +743,28 @@
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
- if (getChildCount() > 0) {
- if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
- fling(-initialVelocity);
- } else {
- if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
- getScrollRange())) {
- postInvalidateOnAnimation();
+ if (shouldOverScrollFling(initialVelocity)) {
+ onOverScrollFling(true, initialVelocity);
+ } else {
+ if (getChildCount() > 0) {
+ if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
+ float currentOverScrollTop = getCurrentOverScrollAmount(true);
+ if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+ fling(-initialVelocity);
+ } else {
+ onOverScrollFling(false, initialVelocity);
+ }
+ } else {
+ if (mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0,
+ getScrollRange())) {
+ postInvalidateOnAnimation();
+ }
}
}
- }
- mActivePointerId = INVALID_POINTER;
- endDrag();
+ mActivePointerId = INVALID_POINTER;
+ endDrag();
+ }
}
break;
case MotionEvent.ACTION_CANCEL:
@@ -658,17 +779,27 @@
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = ev.getActionIndex();
mLastMotionY = (int) ev.getY(index);
+ mDownX = (int) ev.getX(index);
mActivePointerId = ev.getPointerId(index);
break;
}
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
+ mDownX = (int) ev.getX(ev.findPointerIndex(mActivePointerId));
break;
}
return true;
}
+ private void onOverScrollFling(boolean open, int initialVelocity) {
+ if (mOverscrollTopChangedListener != null) {
+ mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
+ }
+ mDontReportNextOverScroll = true;
+ setOverScrollAmount(0.0f, true, false);
+ }
+
/**
* Perform a scroll upwards and adapt the overscroll amounts accordingly
*
@@ -689,11 +820,13 @@
float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
float newScrollY = mOwnScrollY + scrollAmount;
if (newScrollY > range) {
- float currentBottomPixels = getCurrentOverScrolledPixels(false);
- // We overScroll on the top
- setOverScrolledPixels(currentBottomPixels + newScrollY - range,
- false /* onTop */,
- false /* animate */);
+ if (!mExpandedInThisMotion) {
+ float currentBottomPixels = getCurrentOverScrolledPixels(false);
+ // We overScroll on the top
+ setOverScrolledPixels(currentBottomPixels + newScrollY - range,
+ false /* onTop */,
+ false /* animate */);
+ }
mOwnScrollY = range;
scrollAmount = 0.0f;
}
@@ -834,7 +967,7 @@
* @param animate Should an animation be performed.
*/
public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
- setOverScrollAmount(numPixels * RUBBER_BAND_FACTOR, onTop, animate, true);
+ setOverScrollAmount(numPixels * getRubberBandFactor(), onTop, animate, true);
}
/**
@@ -870,17 +1003,21 @@
if (animate) {
mStateAnimator.animateOverScrollToAmount(amount, onTop);
} else {
- setOverScrolledPixels(amount / RUBBER_BAND_FACTOR, onTop);
+ setOverScrolledPixels(amount / getRubberBandFactor(), onTop);
mAmbientState.setOverScrollAmount(amount, onTop);
- requestChildrenUpdate();
if (onTop) {
- float scrollAmount = mOwnScrollY < 0 ? -mOwnScrollY : 0;
- notifyOverscrollTopListener(scrollAmount + amount);
+ notifyOverscrollTopListener(amount);
}
+ requestChildrenUpdate();
}
}
private void notifyOverscrollTopListener(float amount) {
+ mExpandHelper.onlyObserveMovements(amount > 1.0f);
+ if (mDontReportNextOverScroll) {
+ mDontReportNextOverScroll = false;
+ return;
+ }
if (mOverscrollTopChangedListener != null) {
mOverscrollTopChangedListener.onOverscrollTopChanged(amount);
}
@@ -928,7 +1065,7 @@
updateChildren();
float overScrollTop = getCurrentOverScrollAmount(true);
if (mOwnScrollY < 0) {
- notifyOverscrollTopListener(-mOwnScrollY + overScrollTop);
+ notifyOverscrollTopListener(-mOwnScrollY);
} else {
notifyOverscrollTopListener(overScrollTop);
}
@@ -950,6 +1087,7 @@
onTop = true;
newAmount = -mOwnScrollY;
mOwnScrollY = 0;
+ mDontReportNextOverScroll = true;
} else {
onTop = false;
newAmount = mOwnScrollY - scrollRange;
@@ -1085,13 +1223,14 @@
float bottomAmount = getCurrentOverScrollAmount(false);
if (velocityY < 0 && topAmount > 0) {
mOwnScrollY -= (int) topAmount;
+ mDontReportNextOverScroll = true;
setOverScrollAmount(0, true, false);
- mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
* mOverflingDistance + topAmount;
} else if (velocityY > 0 && bottomAmount > 0) {
mOwnScrollY += bottomAmount;
setOverScrollAmount(0, false, false);
- mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+ mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
* mOverflingDistance + bottomAmount;
} else {
// it will be set once we reach the boundary
@@ -1104,6 +1243,47 @@
}
}
+ /**
+ * @return Whether a fling performed on the top overscroll edge lead to the expanded
+ * overScroll view (i.e QS).
+ */
+ private boolean shouldOverScrollFling(int initialVelocity) {
+ float topOverScroll = getCurrentOverScrollAmount(true);
+ return mScrolledToTopOnFirstDown
+ && !mExpandedInThisMotion
+ && topOverScroll > mMinTopOverScrollToEscape
+ && initialVelocity > 0;
+ }
+
+ public void updateTopPadding(float qsHeight, int scrollY, boolean animate) {
+ float start = qsHeight - scrollY + mNotificationTopPadding;
+ float stackHeight = getHeight() - start;
+ if (stackHeight <= mMinStackHeight) {
+ float overflow = mMinStackHeight - stackHeight;
+ stackHeight = mMinStackHeight;
+ start = getHeight() - stackHeight;
+ setTranslationY(overflow);
+ } else {
+ setTranslationY(0);
+ }
+ setTopPadding(clampPadding((int) start), animate);
+ }
+
+ private int clampPadding(int desiredPadding) {
+ return Math.max(desiredPadding, mIntrinsicPadding);
+ }
+
+ private float getRubberBandFactor() {
+ if (mExpandedInThisMotion) {
+ return RUBBER_BAND_FACTOR_AFTER_EXPAND;
+ } else if (mIsExpansionChanging) {
+ return RUBBER_BAND_FACTOR_ON_PANEL_EXPAND;
+ } else if (mScrolledToTopOnFirstDown) {
+ return 1.0f;
+ }
+ return RUBBER_BAND_FACTOR_NORMAL;
+ }
+
private void endDrag() {
setIsBeingDragged(false);
@@ -1119,23 +1299,37 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ initDownStates(ev);
+ boolean expandWantsIt = false;
+ if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
+ expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+ }
boolean scrollWantsIt = false;
- if (!mSwipingInProgress) {
+ if (!mSwipingInProgress && !mExpandingNotification) {
scrollWantsIt = onInterceptTouchEventScroll(ev);
}
boolean swipeWantsIt = false;
- if (!mIsBeingDragged) {
+ if (!mIsBeingDragged
+ && !mExpandingNotification
+ && !mExpandedInThisMotion
+ && !mOnlyScrollingInThisMotion) {
swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
}
- return swipeWantsIt || scrollWantsIt ||
- super.onInterceptTouchEvent(ev);
+ return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+ }
+
+ private void initDownStates(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mExpandedInThisMotion = false;
+ mOnlyScrollingInThisMotion = !mScroller.isFinished();
+ }
}
@Override
protected void onViewRemoved(View child) {
super.onViewRemoved(child);
mStackScrollAlgorithm.notifyChildrenChanged(this);
- if (mChildrenChangingPositions.contains(child)) {
+ if (mChangePositionInProgress) {
// This is only a position change, don't do anything special
return;
}
@@ -1230,7 +1424,7 @@
* @param child The view to be added.
*/
public void generateAddAnimation(View child) {
- if (mIsExpanded && mAnimationsEnabled && !mChildrenChangingPositions.contains(child)) {
+ if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
// Generate Animations
mChildrenToAddAnimated.add(child);
mNeedsAnimation = true;
@@ -1245,10 +1439,14 @@
*/
public void changeViewPosition(View child, int newIndex) {
if (child != null && child.getParent() == this) {
- mChildrenChangingPositions.add(child);
+ mChangePositionInProgress = true;
removeView(child);
addView(child, newIndex);
- mNeedsAnimation = true;
+ mChangePositionInProgress = false;
+ if (mIsExpanded && mAnimationsEnabled) {
+ mChildrenChangingPositions.add(child);
+ mNeedsAnimation = true;
+ }
}
}
@@ -1350,6 +1548,9 @@
}
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
+ if (!isScrollingEnabled()) {
+ return false;
+ }
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true, onMotionEvent will be called and we do the actual
@@ -1366,13 +1567,6 @@
return true;
}
- /*
- * Don't try to intercept touch if we can't scroll anyway.
- */
- if (mOwnScrollY == 0 && getScrollRange() == 0) {
- return false;
- }
-
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
/*
@@ -1398,10 +1592,13 @@
}
final int y = (int) ev.getY(pointerIndex);
+ final int x = (int) ev.getX(pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
- if (yDiff > mTouchSlop) {
+ final int xDiff = Math.abs(x - mDownX);
+ if (yDiff > mTouchSlop && yDiff > xDiff) {
setIsBeingDragged(true);
mLastMotionY = y;
+ mDownX = x;
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
}
@@ -1421,7 +1618,9 @@
* ACTION_DOWN always refers to pointer index 0.
*/
mLastMotionY = y;
+ mDownX = (int) ev.getX();
mActivePointerId = ev.getPointerId(0);
+ mScrolledToTopOnFirstDown = isScrolledToTop();
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
@@ -1468,7 +1667,7 @@
mIsBeingDragged = isDragged;
if (isDragged) {
requestDisallowInterceptTouchEvent(true);
- mSwipeHelper.removeLongPressCallback();
+ removeLongPressCallback();
}
}
@@ -1476,10 +1675,14 @@
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
- mSwipeHelper.removeLongPressCallback();
+ removeLongPressCallback();
}
}
+ public void removeLongPressCallback() {
+ mSwipeHelper.removeLongPressCallback();
+ }
+
@Override
public boolean isScrolledToTop() {
return mOwnScrollY == 0;
@@ -1520,7 +1723,6 @@
mStackScrollAlgorithm.setIsExpanded(isExpanded);
if (!isExpanded) {
mOwnScrollY = 0;
- mSpeedBumpView.collapse();
}
}
@@ -1528,9 +1730,7 @@
public void onHeightChanged(ExpandableView view) {
updateContentHeight();
updateScrollPositionIfNecessary();
- if (mOnHeightChangedListener != null) {
- mOnHeightChangedListener.onHeightChanged(view);
- }
+ notifyHeightChangeListener(view);
requestChildrenUpdate();
}
@@ -1560,7 +1760,7 @@
/**
* See {@link AmbientState#setActivatedChild}.
*/
- public void setActivatedChild(View activatedChild) {
+ public void setActivatedChild(ActivatableNotificationView activatedChild) {
mAmbientState.setActivatedChild(activatedChild);
if (mAnimationsEnabled) {
mActivateNeedsAnimation = true;
@@ -1569,7 +1769,7 @@
requestChildrenUpdate();
}
- public View getActivatedChild() {
+ public ActivatableNotificationView getActivatedChild() {
return mAmbientState.getActivatedChild();
}
@@ -1591,7 +1791,6 @@
int newVisibility = visible ? VISIBLE : GONE;
mSpeedBumpView.setVisibility(newVisibility);
if (visible) {
- mSpeedBumpView.collapse();
// Make invisible to ensure that the appear animation is played.
mSpeedBumpView.setInvisible();
if (!mIsExpansionChanging) {
@@ -1608,6 +1807,38 @@
updateSpeedBump(true);
}
+ public void cancelExpandHelper() {
+ mExpandHelper.cancel();
+ }
+
+ public void setIntrinsicPadding(int intrinsicPadding) {
+ mIntrinsicPadding = intrinsicPadding;
+ }
+
+ /**
+ * @return the y position of the first notification
+ */
+ public float getNotificationsTopY() {
+ return mTopPadding + getTranslationY();
+ }
+
+ public void setTouchEnabled(boolean touchEnabled) {
+ mTouchEnabled = touchEnabled;
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (!mTouchEnabled) {
+ return false;
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
+ public boolean shouldDelayChildPressedState() {
+ return true;
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
@@ -1620,6 +1851,15 @@
*/
public interface OnOverscrollTopChangedListener {
public void onOverscrollTopChanged(float amount);
+
+ /**
+ * Notify a listener that the scroller wants to escape from the scrolling motion and
+ * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS)
+ *
+ * @param velocity The velocity that the Scroller had when over flinging
+ * @param open Should the fling open or close the overscroll view.
+ */
+ public void flingTopOverscroll(float velocity, boolean open);
}
static class AnimationEvent {
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 2b52c7e..cbad9dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -113,7 +113,7 @@
mBottomStackSlowDownLength = context.getResources()
.getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
+ com.android.internal.R.dimen.notification_material_rounded_rect_radius);
}
@@ -131,10 +131,14 @@
algorithmState.scrolledPixelsTop = 0;
algorithmState.itemsInBottomStack = 0.0f;
algorithmState.partialInBottom = 0.0f;
- float topOverScroll = ambientState.getOverScrollAmount(true /* onTop */);
float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
- algorithmState.scrollY = (int) (ambientState.getScrollY() + mCollapsedSize
- + bottomOverScroll - topOverScroll);
+
+ int scrollY = ambientState.getScrollY();
+
+ // Due to the overScroller, the stackscroller can have negative scroll state. This is
+ // already accounted for by the top padding and doesn't need an additional adaption
+ scrollY = Math.max(0, scrollY);
+ algorithmState.scrollY = (int) (scrollY + mCollapsedSize + bottomOverScroll);
updateVisibleChildren(resultState, algorithmState);
@@ -182,7 +186,7 @@
if (!child.isTransparent()) {
// Only update the previous values if we are not transparent,
// otherwise we would clip to a transparent view.
- previousNotificationStart = newYTranslation + child.getClipTopAmount();
+ previousNotificationStart = newYTranslation + state.clipTopAmount;
previousNotificationEnd = newNotificationEnd;
previousNotificationIsSwiped = child.getTranslationX() != 0;
}
@@ -224,11 +228,16 @@
View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
childViewState.dimmed = dimmed;
- childViewState.scale = !dimmed || activatedChild == child
+ boolean isActivatedChild = activatedChild == child;
+ childViewState.scale = !dimmed || isActivatedChild
? 1.0f
: DIMMED_SCALE;
- if (dimmed && activatedChild != null && child != activatedChild) {
- childViewState.alpha *= ACTIVATED_INVERSE_ALPHA;
+ if (dimmed && activatedChild != null) {
+ if (!isActivatedChild) {
+ childViewState.alpha *= ACTIVATED_INVERSE_ALPHA;
+ } else {
+ childViewState.zTranslation += 2.0f * mZDistanceBetweenElements;
+ }
}
}
}
@@ -653,7 +662,7 @@
} else {
// We are expanding the shade, expand it to its full height.
- if (mFirstChildWhileExpanding.getWidth() == 0) {
+ if (!isMaxSizeInitialized(mFirstChildWhileExpanding)) {
// This child was not layouted yet, wait for a layout pass
mFirstChildWhileExpanding
@@ -680,6 +689,14 @@
}
}
+ private boolean isMaxSizeInitialized(ExpandableView child) {
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ return row.isShowingLayoutLayouted();
+ }
+ return child == null || child.getWidth() != 0;
+ }
+
private View findFirstVisibleChild(ViewGroup container) {
int childCount = container.getChildCount();
for (int i = 0; i < childCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 94cb16d..1ad4acc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -160,9 +160,8 @@
}
if(child instanceof SpeedBumpView) {
- float speedBumpEnd = newYTranslation + newHeight;
- performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd,
- newYTranslation);
+ float lineEnd = newYTranslation + newHeight / 2;
+ performSpeedBumpAnimation(i, (SpeedBumpView) child, lineEnd);
}
}
}
@@ -183,20 +182,12 @@
child.setClipBounds(mClipRect);
}
- private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd,
- float speedBumpStart) {
+ private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd) {
View nextChild = getNextChildNotGone(i);
if (nextChild != null) {
ViewState nextState = getViewStateForView(nextChild);
- boolean startIsAboveNext = nextState.yTranslation > speedBumpStart;
+ boolean startIsAboveNext = nextState.yTranslation > speedBumpEnd;
speedBump.animateDivider(startIsAboveNext, null /* onFinishedRunnable */);
-
- // handle expanded case
- if (speedBump.isExpanded()) {
- boolean endIsAboveNext = nextState.yTranslation > speedBumpEnd;
- speedBump.animateExplanationText(endIsAboveNext);
- }
-
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 2edd7d1..225398a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -726,11 +726,21 @@
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentOverScroll = (float) animation.getAnimatedValue();
- mHostLayout.setOverScrollAmount(currentOverScroll, onTop, false /* animate */,
- false /* cancelAnimators */);
+ mHostLayout.setOverScrollAmount(
+ currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */);
}
});
overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
+ overScrollAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (onTop) {
+ mTopOverScrollAnimator = null;
+ } else {
+ mBottomOverScrollAnimator = null;
+ }
+ }
+ });
overScrollAnimator.start();
if (onTop) {
mTopOverScrollAnimator = overScrollAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index faea8de..9260aac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.tv;
import android.os.IBinder;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.BaseStatusBar;
/*
@@ -51,11 +51,11 @@
}
@Override
- public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+ public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
}
@Override
- protected void updateRankingInternal(Ranking ranking) {
+ protected void updateRankingInternal(RankingMap ranking) {
}
@Override
@@ -63,7 +63,7 @@
}
@Override
- public void removeNotificationInternal(String key, Ranking ranking) {
+ public void removeNotificationInternal(String key, RankingMap ranking) {
}
@Override
@@ -147,6 +147,18 @@
}
@Override
+ public void scheduleHeadsUpOpen() {
+ }
+
+ @Override
+ public void scheduleHeadsUpEscalation() {
+ }
+
+ @Override
+ public void scheduleHeadsUpClose() {
+ }
+
+ @Override
protected int getMaxKeyguardNotifications() {
return 0;
}
@@ -164,10 +176,10 @@
}
@Override
- public void onActivated(View view) {
+ public void onActivated(ActivatableNotificationView view) {
}
@Override
- public void onActivationReset(View view) {
+ public void onActivationReset(ActivatableNotificationView view) {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 898b46e..1cab7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -333,6 +333,7 @@
public void onDismiss(DialogInterface dialog) {
mActiveStreamType = -1;
mAudioManager.forceVolumeControlStream(mActiveStreamType);
+ collapse();
}
});
@@ -602,7 +603,7 @@
if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
if (mZenPanel == null) {
mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
- mZenPanel.init(mZenController);
+ mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E');
mZenPanel.setCallback(new ZenModePanel.Callback() {
@Override
public void onMoreSettings() {
@@ -651,6 +652,9 @@
mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
: com.android.systemui.R.drawable.ic_vol_zen_off);
+ if (show && !zen) {
+ collapse();
+ }
} else {
mExpandButton.setVisibility(View.GONE);
mExpandDivider.setVisibility(View.GONE);
@@ -882,10 +886,6 @@
// when the stream is for remote playback, use -1 to reset the stream type evaluation
mAudioManager.forceVolumeControlStream(stream);
- // Showing dialog - use collapsed state
- if (mZenModeCapable) {
- collapse();
- }
if (mDialog != null) {
mDialog.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index c338563..798e7fa 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -18,16 +18,21 @@
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.service.notification.Condition;
+import android.service.notification.ZenModeConfig;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
@@ -39,26 +44,47 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import java.util.Arrays;
-import java.util.HashSet;
+import java.util.Objects;
public class ZenModePanel extends LinearLayout {
- private static final int[] MINUTES = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+ private static final boolean DEBUG = false;
+
+ private static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+ private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
+ private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
+ private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
+
+ private static final int SECONDS_MS = 1000;
+ private static final int MINUTES_MS = 60 * SECONDS_MS;
+
public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
private final Context mContext;
private final LayoutInflater mInflater;
- private final HashSet<RadioButton> mRadioButtons = new HashSet<RadioButton>();
private final H mHandler = new H();
+ private final Favorites mFavorites;
+
+ private char mLogTag = '?';
+ private String mTag;
private LinearLayout mConditions;
- private int mMinutesIndex = Arrays.binarySearch(MINUTES, 60); // default to one hour
private Callback mCallback;
private ZenModeController mController;
private boolean mRequestingConditions;
+ private Uri mExitConditionId;
+ private int mBucketIndex = -1;
+ private boolean mShowing;
public ZenModePanel(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
+ mFavorites = new Favorites();
mInflater = LayoutInflater.from(new ContextThemeWrapper(context, R.style.QSWhiteTheme));
+ updateTag();
+ if (DEBUG) Log.d(mTag, "new ZenModePanel");
+ }
+
+ private void updateTag() {
+ mTag = "ZenModePanel/" + mLogTag + "/" + Integer.toHexString(System.identityHashCode(this));
}
@Override
@@ -74,44 +100,97 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (DEBUG) Log.d(mTag, "onAttachedToWindow");
+ final ViewGroup p = (ViewGroup) getParent();
+ updateShowing(p != null && p.getVisibility() == VISIBLE);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
+ updateShowing(false);
+ }
+
+ @Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
- setRequestingConditions(visibility == VISIBLE);
+ final boolean vis = visibility == VISIBLE;
+ updateShowing(isAttachedToWindow() && vis);
+ }
+
+ private void updateShowing(boolean showing) {
+ if (showing == mShowing) return;
+ mShowing = showing;
+ if (DEBUG) Log.d(mTag, "mShowing=" + mShowing);
+ if (mController != null) {
+ setRequestingConditions(mShowing);
+ }
}
/** Start or stop requesting relevant zen mode exit conditions */
private void setRequestingConditions(boolean requesting) {
if (mRequestingConditions == requesting) return;
+ if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting);
mRequestingConditions = requesting;
- if (mRequestingConditions) {
- mController.addCallback(mZenCallback);
- } else {
- mController.removeCallback(mZenCallback);
+ if (mController != null) {
+ mController.requestConditions(mRequestingConditions);
}
- mController.requestConditions(mRequestingConditions);
+ if (mRequestingConditions) {
+ Condition timeCondition = parseExistingTimeCondition(mExitConditionId);
+ if (timeCondition != null) {
+ mBucketIndex = -1;
+ } else {
+ mBucketIndex = DEFAULT_BUCKET_INDEX;
+ timeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
+ }
+ if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
+ bind(timeCondition, mConditions.getChildAt(0));
+ handleUpdateConditions(new Condition[0]);
+ } else {
+ mConditions.removeAllViews();
+ }
}
- public void init(ZenModeController controller) {
+ public void init(ZenModeController controller, char logTag) {
mController = controller;
+ mLogTag = logTag;
+ updateTag();
+ mExitConditionId = mController.getExitConditionId();
+ if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
mConditions.removeAllViews();
- bind(updateTimeCondition(), mConditions.getChildAt(0));
- handleUpdateConditions(new Condition[0]);
+ mController.addCallback(mZenCallback);
+ if (mShowing) {
+ setRequestingConditions(true);
+ }
}
public void setCallback(Callback callback) {
mCallback = callback;
}
- private Condition updateTimeCondition() {
- final int minutes = MINUTES[mMinutesIndex];
- final long millis = System.currentTimeMillis() + minutes * 60 * 1000;
- final Uri id = new Uri.Builder().scheme(Condition.SCHEME).authority("android")
- .appendPath("countdown").appendPath(Long.toString(millis)).build();
- final int num = minutes < 60 ? minutes : minutes / 60;
+ private Condition parseExistingTimeCondition(Uri conditionId) {
+ final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
+ if (time == 0) return null;
+ final long span = time - System.currentTimeMillis();
+ if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null;
+ return timeCondition(time, Math.round(span / (float)MINUTES_MS));
+ }
+
+ private Condition newTimeCondition(int minutesFromNow) {
+ final long now = System.currentTimeMillis();
+ return timeCondition(now + minutesFromNow * MINUTES_MS, minutesFromNow);
+ }
+
+ private Condition timeCondition(long time, int minutes) {
+ final int num = minutes < 60 ? minutes : Math.round(minutes / 60f);
final int resId = minutes < 60
? R.plurals.zen_mode_duration_minutes
: R.plurals.zen_mode_duration_hours;
final String caption = mContext.getResources().getQuantityString(resId, num, num);
+ final Uri id = ZenModeConfig.toCountdownConditionId(time);
return new Condition(id, caption, "", "", 0, Condition.STATE_TRUE,
Condition.FLAG_RELEVANT_NOW);
}
@@ -125,14 +204,40 @@
bind(conditions[i], mConditions.getChildAt(i + 1));
}
bind(null, mConditions.getChildAt(newCount + 1));
+ checkForDefault();
}
- private void editTimeCondition(int delta) {
- final int i = mMinutesIndex + delta;
- if (i < 0 || i >= MINUTES.length) return;
- mMinutesIndex = i;
- final Condition c = updateTimeCondition();
- bind(c, mConditions.getChildAt(0));
+ private ConditionTag getConditionTagAt(int index) {
+ return (ConditionTag) mConditions.getChildAt(index).getTag();
+ }
+
+ private void checkForDefault() {
+ // are we left without anything selected? if so, set a default
+ for (int i = 0; i < mConditions.getChildCount(); i++) {
+ if (getConditionTagAt(i).rb.isChecked()) {
+ return;
+ }
+ }
+ if (DEBUG) Log.d(mTag, "Selecting a default");
+ final int favoriteIndex = mFavorites.getMinuteIndex();
+ if (favoriteIndex == -1) {
+ getConditionTagAt(mConditions.getChildCount() - 1).rb.setChecked(true);
+ } else {
+ final Condition c = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
+ mBucketIndex = favoriteIndex;
+ bind(c, mConditions.getChildAt(0));
+ getConditionTagAt(0).rb.setChecked(true);
+ }
+ }
+
+ private void handleExitConditionChanged(Uri exitCondition) {
+ mExitConditionId = exitCondition;
+ if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitConditionId);
+ final int N = mConditions.getChildCount();
+ for (int i = 0; i < N; i++) {
+ final ConditionTag tag = getConditionTagAt(i);
+ tag.rb.setChecked(Objects.equals(tag.conditionId, exitCondition));
+ }
}
private void bind(final Condition condition, View convertView) {
@@ -140,23 +245,31 @@
final View row;
if (convertView == null) {
row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
+ if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition);
mConditions.addView(row);
} else {
row = convertView;
}
- final int position = mConditions.indexOfChild(row);
- final RadioButton rb = (RadioButton) row.findViewById(android.R.id.checkbox);
- mRadioButtons.add(rb);
- rb.setEnabled(enabled);
- rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ final ConditionTag tag =
+ row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag();
+ row.setTag(tag);
+ if (tag.rb == null) {
+ tag.rb = (RadioButton) row.findViewById(android.R.id.checkbox);
+ }
+ tag.conditionId = condition != null ? condition.id : null;
+ tag.rb.setEnabled(enabled);
+ tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- for (RadioButton otherButton : mRadioButtons) {
- if (otherButton == rb) continue;
- otherButton.setChecked(false);
+ if (mShowing && isChecked) {
+ if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.conditionId);
+ final int N = mConditions.getChildCount();
+ for (int i = 0; i < N; i++) {
+ ConditionTag childTag = getConditionTagAt(i);
+ if (childTag == tag) continue;
+ childTag.rb.setChecked(false);
}
- mController.select(condition);
+ select(tag.conditionId);
fireInteraction();
}
}
@@ -173,9 +286,7 @@
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- rb.setChecked(true);
- editTimeCondition(-1);
- fireInteraction();
+ onClickTimeButton(row, tag, false /*down*/);
}
});
@@ -183,29 +294,79 @@
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- rb.setChecked(true);
- editTimeCondition(1);
- fireInteraction();
+ onClickTimeButton(row, tag, true /*up*/);
}
});
title.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- rb.setChecked(true);
+ tag.rb.setChecked(true);
fireInteraction();
}
});
- if (position == 0) {
- button1.setEnabled(mMinutesIndex > 0);
- button2.setEnabled(mMinutesIndex < MINUTES.length - 1);
- button1.setImageAlpha(button1.isEnabled() ? 0xff : 0x7f);
- button2.setImageAlpha(button2.isEnabled() ? 0xff : 0x7f);
+
+ final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId);
+ if (time > 0) {
+ if (mBucketIndex > -1) {
+ button1.setEnabled(mBucketIndex > 0);
+ button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1);
+ } else {
+ final long span = time - System.currentTimeMillis();
+ button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS);
+ final Condition maxCondition = newTimeCondition(MAX_BUCKET_MINUTES);
+ button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary));
+ }
+
+ button1.setAlpha(button1.isEnabled() ? 1f : .5f);
+ button2.setAlpha(button2.isEnabled() ? 1f : .5f);
} else {
button1.setVisibility(View.GONE);
button2.setVisibility(View.GONE);
}
- if (position == 0 && mConditions.getChildCount() == 1) {
- rb.setChecked(true);
+ }
+
+ private void onClickTimeButton(View row, ConditionTag tag, boolean up) {
+ Condition newCondition = null;
+ final int N = MINUTE_BUCKETS.length;
+ if (mBucketIndex == -1) {
+ // not on a known index, search for the next or prev bucket by time
+ final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId);
+ final long now = System.currentTimeMillis();
+ for (int i = 0; i < N; i++) {
+ int j = up ? i : N - 1 - i;
+ final int bucketMinutes = MINUTE_BUCKETS[j];
+ final long bucketTime = now + bucketMinutes * MINUTES_MS;
+ if (up && bucketTime > time || !up && bucketTime < time) {
+ mBucketIndex = j;
+ newCondition = timeCondition(bucketTime, bucketMinutes);
+ break;
+ }
+ }
+ if (newCondition == null) {
+ mBucketIndex = DEFAULT_BUCKET_INDEX;
+ newCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
+ }
+ } else {
+ // on a known index, simply increment or decrement
+ mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
+ newCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
+ }
+ bind(newCondition, row);
+ tag.rb.setChecked(true);
+ select(newCondition.id);
+ fireInteraction();
+ }
+
+ private void select(Uri conditionId) {
+ if (DEBUG) Log.d(mTag, "select " + conditionId);
+ if (mController != null) {
+ mController.setExitConditionId(conditionId);
+ }
+ mExitConditionId = conditionId;
+ if (conditionId == null) {
+ mFavorites.setMinuteIndex(-1);
+ } else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
+ mFavorites.setMinuteIndex(mBucketIndex);
}
}
@@ -226,10 +387,16 @@
public void onConditionsChanged(Condition[] conditions) {
mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
}
+
+ @Override
+ public void onExitConditionChanged(Uri exitConditionId) {
+ mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitConditionId).sendToTarget();
+ }
};
private final class H extends Handler {
private static final int UPDATE_CONDITIONS = 1;
+ private static final int EXIT_CONDITION_CHANGED = 2;
private H() {
super(Looper.getMainLooper());
@@ -239,6 +406,8 @@
public void handleMessage(Message msg) {
if (msg.what == UPDATE_CONDITIONS) {
handleUpdateConditions((Condition[])msg.obj);
+ } else if (msg.what == EXIT_CONDITION_CHANGED) {
+ handleExitConditionChanged((Uri)msg.obj);
}
}
}
@@ -247,4 +416,51 @@
void onMoreSettings();
void onInteraction();
}
+
+ // used as the view tag on condition rows
+ private static class ConditionTag {
+ RadioButton rb;
+ Uri conditionId;
+ }
+
+ private final class Favorites implements OnSharedPreferenceChangeListener {
+ private static final String KEY_MINUTE_INDEX = "minuteIndex";
+
+ private int mMinuteIndex;
+
+ private Favorites() {
+ prefs().registerOnSharedPreferenceChangeListener(this);
+ updateMinuteIndex();
+ }
+
+ public int getMinuteIndex() {
+ return mMinuteIndex;
+ }
+
+ public void setMinuteIndex(int minuteIndex) {
+ minuteIndex = clamp(minuteIndex);
+ if (minuteIndex == mMinuteIndex) return;
+ mMinuteIndex = clamp(minuteIndex);
+ if (DEBUG) Log.d(mTag, "Setting favorite minute index: " + mMinuteIndex);
+ prefs().edit().putInt(KEY_MINUTE_INDEX, mMinuteIndex).apply();
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ updateMinuteIndex();
+ }
+
+ private SharedPreferences prefs() {
+ return mContext.getSharedPreferences(ZenModePanel.class.getSimpleName(), 0);
+ }
+
+ private void updateMinuteIndex() {
+ mMinuteIndex = clamp(prefs().getInt(KEY_MINUTE_INDEX, DEFAULT_BUCKET_INDEX));
+ if (DEBUG) Log.d(mTag, "Favorite minute index: " + mMinuteIndex);
+ }
+
+ private int clamp(int index) {
+ return Math.max(-1, Math.min(MINUTE_BUCKETS.length - 1, index));
+ }
+ }
}
diff --git a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
index 764156d..66ece4f 100644
--- a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
+++ b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
@@ -283,9 +283,6 @@
} catch (FileNotFoundException e) {
Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
return null;
- } catch (IOException e) {
- Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
- return null;
}
}
@Override
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 5dc9e58..5e1aa3b 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -46,6 +46,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -92,6 +93,7 @@
import android.view.ViewRootImpl;
import android.view.ViewStub;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -117,6 +119,9 @@
private final static String TAG = "PhoneWindow";
private final static boolean SWEEP_OPEN_MENU = false;
+
+ private final static int DEFAULT_BACKGROUND_FADE_DURATION_MS = 300;
+
/**
* Simple callback used by the context menu and its submenus. The options
* menu submenus do not use this (their behavior is more complex).
@@ -213,7 +218,7 @@
private int mTitleColor = 0;
private boolean mAlwaysReadCloseOnTouchAttr = false;
-
+
private ContextMenuBuilder mContextMenu;
private MenuDialogHelper mContextMenuHelper;
private boolean mClosingActionMenu;
@@ -245,6 +250,7 @@
private Transition mSharedElementExitTransition;
private Boolean mAllowExitTransitionOverlap;
private Boolean mAllowEnterTransitionOverlap;
+ private long mBackgroundFadeDurationMillis = -1;
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -475,7 +481,7 @@
if (st.isPrepared) {
return true;
}
-
+
if ((mPreparedPanel != null) && (mPreparedPanel != st)) {
// Another Panel is prepared and possibly open, so close it
closePanel(mPreparedPanel, false);
@@ -528,7 +534,7 @@
return false;
}
-
+
st.refreshMenuContent = false;
}
@@ -620,7 +626,7 @@
// Causes the decor view to be recreated
st.refreshDecorView = true;
-
+
st.clearMenuPresenters();
}
@@ -872,7 +878,7 @@
}
st.refreshMenuContent = true;
st.refreshDecorView = true;
-
+
// Prepare the options panel if we have an action bar
if ((featureId == FEATURE_ACTION_BAR || featureId == FEATURE_OPTIONS_PANEL)
&& mDecorContentParent != null) {
@@ -883,7 +889,7 @@
}
}
}
-
+
/**
* Called when the panel key is pushed down.
* @param featureId The feature ID of the relevant panel (defaults to FEATURE_OPTIONS_PANEL}.
@@ -892,7 +898,7 @@
*/
public final boolean onKeyDownPanel(int featureId, KeyEvent event) {
final int keyCode = event.getKeyCode();
-
+
if (event.getRepeatCount() == 0) {
// The panel key was pushed, so set the chording key
mPanelChordingKey = keyCode;
@@ -919,7 +925,7 @@
if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null)) {
return;
}
-
+
boolean playSoundEffect = false;
final PanelFeatureState st = getPanelState(featureId, true);
if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
@@ -1159,22 +1165,40 @@
protected boolean initializePanelMenu(final PanelFeatureState st) {
Context context = getContext();
- // If we have an action bar, initialize the menu with a context themed for it.
+ // If we have an action bar, initialize the menu with the right theme.
if ((st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR) &&
mDecorContentParent != null) {
- TypedValue outValue = new TypedValue();
- Resources.Theme currentTheme = context.getTheme();
- currentTheme.resolveAttribute(com.android.internal.R.attr.actionBarWidgetTheme,
- outValue, true);
- final int targetThemeRes = outValue.resourceId;
+ final TypedValue outValue = new TypedValue();
+ final Theme baseTheme = context.getTheme();
+ baseTheme.resolveAttribute(com.android.internal.R.attr.actionBarTheme, outValue, true);
- if (targetThemeRes != 0 && context.getThemeResId() != targetThemeRes) {
- context = new ContextThemeWrapper(context, targetThemeRes);
+ Theme widgetTheme = null;
+ if (outValue.resourceId != 0) {
+ widgetTheme = context.getResources().newTheme();
+ widgetTheme.setTo(baseTheme);
+ widgetTheme.applyStyle(outValue.resourceId, true);
+ widgetTheme.resolveAttribute(
+ com.android.internal.R.attr.actionBarWidgetTheme, outValue, true);
+ } else {
+ baseTheme.resolveAttribute(
+ com.android.internal.R.attr.actionBarWidgetTheme, outValue, true);
+ }
+
+ if (outValue.resourceId != 0) {
+ if (widgetTheme == null) {
+ widgetTheme = context.getResources().newTheme();
+ widgetTheme.setTo(baseTheme);
+ }
+ widgetTheme.applyStyle(outValue.resourceId, true);
+ }
+
+ if (widgetTheme != null) {
+ context = new ContextThemeWrapper(context, 0);
+ context.getTheme().setTo(widgetTheme);
}
}
final MenuBuilder menu = new MenuBuilder(context);
-
menu.setCallback(this);
st.setMenu(menu);
@@ -1665,7 +1689,7 @@
mDecor != null ? mDecor.getKeyDispatcherState() : null;
//Log.i(TAG, "Key down: repeat=" + event.getRepeatCount()
// + " flags=0x" + Integer.toHexString(event.getFlags()));
-
+
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -1727,7 +1751,7 @@
}
//Log.i(TAG, "Key up: repeat=" + event.getRepeatCount()
// + " flags=0x" + Integer.toHexString(event.getFlags()));
-
+
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -2278,7 +2302,7 @@
if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
return;
}
-
+
// if we are showing a feature that should be announced and one child
// make this child the event source since this is the feature itself
// otherwise the callback will take over and announce its client
@@ -2608,15 +2632,15 @@
}
@Override
- protected boolean fitSystemWindows(Rect insets) {
- mFrameOffsets.set(insets);
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ mFrameOffsets.set(insets.getSystemWindowInsets());
updateColorViews(insets);
- updateStatusGuard(insets);
+ insets = updateStatusGuard(insets);
updateNavigationGuard(insets);
if (getForeground() != null) {
drawableChanged();
}
- return super.fitSystemWindows(insets);
+ return insets;
}
@Override
@@ -2624,14 +2648,14 @@
return false;
}
- private void updateColorViews(Rect insets) {
+ private void updateColorViews(WindowInsets insets) {
if (mIsFloating || !ActivityManager.isHighEndGfx()) {
// No colors on floating windows or low end devices :(
return;
}
if (insets != null) {
- mLastTopInset = insets.top;
- mLastBottomInset = insets.bottom;
+ mLastTopInset = insets.getSystemWindowInsetTop();
+ mLastBottomInset = insets.getSystemWindowInsetBottom();
}
mStatusColorView = updateColorViewInt(mStatusColorView,
SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
@@ -2670,7 +2694,7 @@
return view;
}
- private void updateStatusGuard(Rect insets) {
+ private WindowInsets updateStatusGuard(WindowInsets insets) {
boolean showStatusGuard = false;
// Show the status guard when the non-overlay contextual action bar is showing
if (mActionModeView != null) {
@@ -2682,9 +2706,9 @@
&& mActionModeView.isShown();
if (nonOverlayShown) {
// set top margin to top insets, show status guard
- if (mlp.topMargin != insets.top) {
+ if (mlp.topMargin != insets.getSystemWindowInsetTop()) {
mlpChanged = true;
- mlp.topMargin = insets.top;
+ mlp.topMargin = insets.getSystemWindowInsetTop();
if (mStatusGuard == null) {
mStatusGuard = new View(mContext);
mStatusGuard.setBackgroundColor(mContext.getResources()
@@ -2700,7 +2724,8 @@
}
}
}
- insets.top = 0; // consume top insets
+ insets = insets.consumeSystemWindowInsets(
+ false, true /* top */, false, false);
showStatusGuard = true;
} else {
// reset top margin
@@ -2717,9 +2742,10 @@
if (mStatusGuard != null) {
mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
}
+ return insets;
}
- private void updateNavigationGuard(Rect insets) {
+ private void updateNavigationGuard(WindowInsets insets) {
// IMEs lay out below the nav bar, but the content view must not (for back compat)
if (getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
// prevent the content view from including the nav bar height
@@ -2727,7 +2753,7 @@
if (mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
MarginLayoutParams mlp =
(MarginLayoutParams) mContentParent.getLayoutParams();
- mlp.bottomMargin = insets.bottom;
+ mlp.bottomMargin = insets.getSystemWindowInsetBottom();
mContentParent.setLayoutParams(mlp);
}
}
@@ -2737,11 +2763,11 @@
mNavigationGuard.setBackgroundColor(mContext.getResources()
.getColor(R.color.input_method_navigation_guard));
addView(mNavigationGuard, indexOfChild(mNavigationColorView), new LayoutParams(
- LayoutParams.MATCH_PARENT, insets.bottom,
+ LayoutParams.MATCH_PARENT, insets.getSystemWindowInsetBottom(),
Gravity.START | Gravity.BOTTOM));
} else {
LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams();
- lp.height = insets.bottom;
+ lp.height = insets.getSystemWindowInsetBottom();
mNavigationGuard.setLayoutParams(lp);
}
}
@@ -2829,13 +2855,13 @@
hackTurnOffWindowResizeAnim(bg == null || bg.getOpacity()
!= PixelFormat.OPAQUE);
}
-
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
-
+
updateWindowResizeState();
-
+
final Callback cb = getCallback();
if (cb != null && !isDestroyed() && mFeatureId < 0) {
cb.onAttachedToWindow();
@@ -2856,7 +2882,7 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
-
+
final Callback cb = getCallback();
if (cb != null && mFeatureId < 0) {
cb.onDetachedFromWindow();
@@ -2890,19 +2916,19 @@
public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
return mFeatureId < 0 ? mTakeSurfaceCallback : null;
}
-
+
public InputQueue.Callback willYouTakeTheInputQueue() {
return mFeatureId < 0 ? mTakeInputQueueCallback : null;
}
-
+
public void setSurfaceType(int type) {
PhoneWindow.this.setType(type);
}
-
+
public void setSurfaceFormat(int format) {
PhoneWindow.this.setFormat(format);
}
-
+
public void setSurfaceKeepScreenOn(boolean keepOn) {
if (keepOn) PhoneWindow.this.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
else PhoneWindow.this.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@@ -3119,7 +3145,7 @@
setCloseOnTouchOutsideIfNotSet(true);
}
}
-
+
WindowManager.LayoutParams params = getAttributes();
if (!hasSoftInputMode()) {
@@ -3400,6 +3426,12 @@
com.android.internal.R.styleable.
Window_windowAllowExitTransitionOverlap, true);
}
+ if (mBackgroundFadeDurationMillis < 0) {
+ mBackgroundFadeDurationMillis = getWindowStyle().getInteger(
+ com.android.internal.R.styleable.
+ Window_windowTransitionBackgroundFadeDuration,
+ DEFAULT_BACKGROUND_FADE_DURATION_MS);
+ }
}
}
}
@@ -3419,9 +3451,8 @@
private Drawable loadImageURI(Uri uri) {
try {
- final Context context = getContext();
- return Drawable.createFromStreamThemed(
- context.getContentResolver().openInputStream(uri), null, context.getTheme());
+ return Drawable.createFromStream(
+ getContext().getContentResolver().openInputStream(uri), null);
} catch (Exception e) {
Log.w(TAG, "Unable to open content: " + uri);
}
@@ -3807,6 +3838,20 @@
return (mAllowExitTransitionOverlap == null) ? true : mAllowExitTransitionOverlap;
}
+ @Override
+ public long getTransitionBackgroundFadeDuration() {
+ return (mBackgroundFadeDurationMillis < 0) ? DEFAULT_BACKGROUND_FADE_DURATION_MS
+ : mBackgroundFadeDurationMillis;
+ }
+
+ @Override
+ public void setTransitionBackgroundFadeDuration(long fadeDurationMillis) {
+ if (fadeDurationMillis < 0) {
+ throw new IllegalArgumentException("negative durations are not allowed");
+ }
+ mBackgroundFadeDurationMillis = fadeDurationMillis;
+ }
+
private static final class DrawableFeatureState {
DrawableFeatureState(int _featureId) {
featureId = _featureId;
@@ -3898,11 +3943,11 @@
boolean refreshDecorView;
boolean refreshMenuContent;
-
+
boolean wasLastOpen;
-
+
boolean wasLastExpanded;
-
+
/**
* Contains the state of the menu when told to freeze.
*/
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0d39586..5598972 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -91,6 +91,7 @@
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.WindowManagerInternal;
import android.view.WindowManagerPolicy;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -102,14 +103,17 @@
import com.android.internal.policy.IKeyguardServiceConstants;
import com.android.internal.policy.PolicyManager;
import com.android.internal.policy.impl.keyguard.KeyguardServiceDelegate;
+import com.android.internal.policy.impl.keyguard.KeyguardServiceDelegate.ShowListener;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.telephony.ITelephony;
import com.android.internal.widget.PointerLocationView;
+import com.android.server.LocalServices;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashSet;
import static android.view.WindowManager.LayoutParams.*;
@@ -131,6 +135,7 @@
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_STARTING_WINDOW = false;
+ static final boolean DEBUG_WAKEUP = false;
static final boolean SHOW_STARTING_ANIMATIONS = true;
static final boolean SHOW_PROCESSES_ON_ALT_MENU = false;
@@ -223,6 +228,7 @@
Context mContext;
IWindowManager mWindowManager;
WindowManagerFuncs mWindowManagerFuncs;
+ WindowManagerInternal mWindowManagerInternal;
PowerManager mPowerManager;
IStatusBarService mStatusBarService;
boolean mPreloadedRecentApps;
@@ -264,6 +270,25 @@
int[] mNavigationBarWidthForRotation = new int[4];
KeyguardServiceDelegate mKeyguardDelegate;
+ // The following are only accessed on the mHandler thread.
+ boolean mKeyguardDrawComplete;
+ boolean mWindowManagerDrawComplete;
+ ArrayList<ScreenOnListener> mScreenOnListeners = new ArrayList<ScreenOnListener>();
+ final IRemoteCallback mWindowManagerDrawCallback = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) {
+ if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display!");
+ mHandler.sendEmptyMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE);
+ }
+ };
+ final ShowListener mKeyguardDelegateCallback = new ShowListener() {
+ @Override
+ public void onShown(IBinder windowToken) {
+ if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onShown.");
+ mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+ }
+ };
+
GlobalActions mGlobalActions;
volatile boolean mPowerKeyHandled; // accessed from input reader and handler thread
boolean mPendingPowerKeyUpCanceled;
@@ -271,6 +296,7 @@
WindowState mLastInputMethodWindow = null;
WindowState mLastInputMethodTargetWindow = null;
+ boolean mRecentsVisible;
int mRecentAppsHeldModifiers;
boolean mLanguageSwitchKeyPressed;
@@ -280,6 +306,7 @@
boolean mSystemReady;
boolean mSystemBooted;
boolean mHdmiPlugged;
+ IUiModeManager mUiModeManager;
int mUiMode;
int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
int mLidOpenRotation;
@@ -409,6 +436,12 @@
* be done once per window. */
private WindowState mWinDismissingKeyguard;
+ /** The window that is currently showing "over" the keyguard. If there is an app window
+ * belonging to another app on top of this the keyguard shows. If there is a fullscreen
+ * app window under this, still dismiss the keyguard but don't show the app underneath. Show
+ * the wallpaper. */
+ private WindowState mWinShowWhenLocked;
+
boolean mShowingLockscreen;
boolean mShowingDream;
boolean mDreamingLockscreen;
@@ -421,6 +454,7 @@
boolean mSearchKeyShortcutPending;
boolean mConsumeSearchKeyUp;
boolean mAssistKeyLongPressed;
+ boolean mPendingMetaAction;
// support for activating the lock screen while the screen is on
boolean mAllowLockscreenWhenOn;
@@ -486,6 +520,10 @@
private static final int MSG_DISABLE_POINTER_LOCATION = 2;
private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
private static final int MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK = 4;
+ private static final int MSG_KEYGUARD_DRAWN_COMPLETE = 5;
+ private static final int MSG_KEYGUARD_DRAWN_TIMEOUT = 6;
+ private static final int MSG_WINDOW_MANAGER_DRAWN_COMPLETE = 7;
+ private static final int MSG_WAKING_UP = 8;
private class PolicyHandler extends Handler {
@Override
@@ -503,6 +541,25 @@
case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
break;
+ case MSG_KEYGUARD_DRAWN_COMPLETE:
+ if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete");
+ mKeyguardDrawComplete = true;
+ finishScreenTurningOn();
+ break;
+ case MSG_KEYGUARD_DRAWN_TIMEOUT:
+ Slog.w(TAG, "Keyguard drawn timeout. Setting mKeyguardDrawComplete");
+ mKeyguardDrawComplete = true;
+ finishScreenTurningOn();
+ break;
+ case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:
+ if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mWindowManagerDrawComplete");
+ mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
+ mWindowManagerDrawComplete = true;
+ finishScreenTurningOn();
+ break;
+ case MSG_WAKING_UP:
+ handleWakingUp((ScreenOnListener) msg.obj);
+ break;
}
}
}
@@ -876,6 +933,8 @@
mContext = context;
mWindowManager = windowManager;
mWindowManagerFuncs = windowManagerFuncs;
+ mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+
mHandler = new PolicyHandler();
mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
mOrientationListener = new MyOrientationListener(mContext, mHandler);
@@ -1617,8 +1676,9 @@
}
@Override
- public boolean doesForceHide(WindowManager.LayoutParams attrs) {
- return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
+ public boolean isForceHiding(WindowManager.LayoutParams attrs) {
+ return (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
+ (isKeyguardHostWindow(attrs) && isKeyguardSecureIncludingHidden());
}
@Override
@@ -2038,6 +2098,12 @@
}
}
+ // Cancel any pending meta actions if we see any other keys being pressed between the down
+ // of the meta key and its corresponding up.
+ if (mPendingMetaAction && keyCode != KeyEvent.KEYCODE_META_LEFT) {
+ mPendingMetaAction = false;
+ }
+
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
// it handle it, because that gives us the correct 5 second
@@ -2232,6 +2298,14 @@
mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
return -1;
+ } else if (keyCode == KeyEvent.KEYCODE_META_LEFT) {
+ if (down) {
+ mPendingMetaAction = true;
+ } else if (mPendingMetaAction) {
+ mPendingMetaAction = false;
+ launchAssistAction();
+ }
+ return -1;
}
// Shortcuts are invoked through Search+key, so intercept those here
@@ -2341,6 +2415,11 @@
return -1;
}
+ // Reserve all the META modifier combos for system behavior
+ if ((metaState & KeyEvent.META_META_LEFT_ON) != 0) {
+ return -1;
+ }
+
// Let the application handle the key.
return 0;
}
@@ -2562,6 +2641,11 @@
}
}
});
+ } else if (mRecentsVisible) {
+ // Recents is started on top of Home, so when we launch home while recents is open, let
+ // it do its own animation and then finish itself
+ sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+ hideRecentApps(false);
} else {
// no keyguard stuff to worry about, just launch home!
try {
@@ -2653,6 +2737,7 @@
public int adjustSystemUiVisibilityLw(int visibility) {
mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
+ mRecentsVisible = (visibility & View.RECENT_APPS_VISIBLE) > 0;
// Reset any bits in mForceClearingStatusBarVisibility that
// are now clear.
@@ -3042,10 +3127,11 @@
/** {@inheritDoc} */
@Override
- public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs,
- WindowState attached) {
+ public void layoutWindowLw(WindowState win, WindowState attached) {
// we've already done the status bar
- if (win == mStatusBar || win == mNavigationBar) {
+ final WindowManager.LayoutParams attrs = win.getAttrs();
+ if ((win == mStatusBar && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) == 0) ||
+ win == mNavigationBar) {
return;
}
final boolean isDefaultDisplay = win.isDefaultDisplay();
@@ -3323,7 +3409,8 @@
= mOverscanScreenTop + mOverscanScreenHeight;
} else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
- && (attrs.type == TYPE_TOAST
+ && (attrs.type == TYPE_STATUS_BAR
+ || attrs.type == TYPE_TOAST
|| (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
&& attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) {
// Asking for layout as if the nav bar is hidden, lets the
@@ -3525,12 +3612,12 @@
mDismissKeyguard = DISMISS_KEYGUARD_NONE;
mShowingLockscreen = false;
mShowingDream = false;
+ mWinShowWhenLocked = null;
}
/** {@inheritDoc} */
@Override
- public void applyPostLayoutPolicyLw(WindowState win,
- WindowManager.LayoutParams attrs) {
+ public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw="
+ win.isVisibleOrBehindKeyguardLw());
final int fl = PolicyControl.getWindowFlags(win, attrs);
@@ -3568,9 +3655,17 @@
final boolean showWhenLocked = (fl & FLAG_SHOW_WHEN_LOCKED) != 0;
final boolean dismissKeyguard = (fl & FLAG_DISMISS_KEYGUARD) != 0;
+ final boolean secureKeyguard = isKeyguardSecure();
if (appWindow) {
- if (showWhenLocked || (dismissKeyguard && !isKeyguardSecure())) {
+ if (showWhenLocked || (dismissKeyguard && !secureKeyguard)) {
+ // Remove any previous windows with the same appToken.
mAppsToBeHidden.remove(win.getAppToken());
+ if (mAppsToBeHidden.isEmpty() && showWhenLocked &&
+ isKeyguardSecureIncludingHidden()) {
+ mWinShowWhenLocked = win;
+ mHideLockScreen = true;
+ mForceStatusBarFromKeyguard = false;
+ }
} else {
mAppsToBeHidden.add(win.getAppToken());
}
@@ -3592,13 +3687,18 @@
mDismissKeyguard = mWinDismissingKeyguard == win ?
DISMISS_KEYGUARD_CONTINUE : DISMISS_KEYGUARD_START;
mWinDismissingKeyguard = win;
- mForceStatusBarFromKeyguard = mShowingLockscreen && isKeyguardSecure();
+ mForceStatusBarFromKeyguard = mShowingLockscreen && secureKeyguard;
}
}
if ((fl & FLAG_ALLOW_LOCK_WHILE_SCREEN_ON) != 0) {
mAllowLockscreenWhenOn = true;
}
}
+
+ if (mWinShowWhenLocked != null &&
+ mWinShowWhenLocked.getAppToken() != win.getAppToken()) {
+ win.hideLw(false);
+ }
}
}
}
@@ -3606,6 +3706,16 @@
/** {@inheritDoc} */
@Override
public int finishPostLayoutPolicyLw() {
+ if (mWinShowWhenLocked != null &&
+ mWinShowWhenLocked != mTopFullscreenOpaqueWindowState) {
+ // A dialog is dismissing the keyguard. Put the wallpaper behind it and hide the
+ // fullscreen window.
+ // TODO: Make sure FLAG_SHOW_WALLPAPER is restored when dialog is dismissed. Or not.
+ mWinShowWhenLocked.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+ mTopFullscreenOpaqueWindowState.hideLw(false);
+ mTopFullscreenOpaqueWindowState = mWinShowWhenLocked;
+ }
+
int changes = 0;
boolean topIsFullscreen = false;
@@ -4008,11 +4118,6 @@
+ " policyFlags=" + Integer.toHexString(policyFlags));
}
- if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
- && event.getRepeatCount() == 0) {
- performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
- }
-
// Basic policy based on interactive state.
int result;
boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
@@ -4035,6 +4140,10 @@
return result;
}
+ boolean useHapticFeedback = down
+ && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
+ && event.getRepeatCount() == 0;
+
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -4181,6 +4290,9 @@
case KeyEvent.KEYCODE_SLEEP: {
result &= ~ACTION_PASS_TO_USER;
+ if (!mPowerManager.isInteractive()) {
+ useHapticFeedback = false; // suppress feedback if already non-interactive
+ }
mPowerManager.goToSleep(event.getEventTime());
isWakeKey = false;
break;
@@ -4247,6 +4359,10 @@
}
}
+ if (useHapticFeedback) {
+ performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
+ }
+
if (isWakeKey) {
mPowerManager.wakeUp(event.getEventTime());
}
@@ -4464,10 +4580,15 @@
@Override
public void wakingUp(final ScreenOnListener screenOnListener) {
EventLog.writeEvent(70000, 1);
- if (false) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.i(TAG, "Screen turning on...", here);
+ if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on...",
+ new RuntimeException("here").fillInStackTrace());
+ mHandler.obtainMessage(MSG_WAKING_UP, screenOnListener).sendToTarget();
+ }
+
+ // Called on the mHandler thread.
+ private void handleWakingUp(final ScreenOnListener screenOnListener) {
+ if (screenOnListener != null) {
+ mScreenOnListeners.add(screenOnListener);
}
synchronized (mLock) {
@@ -4477,51 +4598,28 @@
updateLockScreenTimeout();
}
- waitForKeyguard(screenOnListener);
- }
-
- private void waitForKeyguard(final ScreenOnListener screenOnListener) {
+ mKeyguardDrawComplete = false;
+ mWindowManagerDrawComplete = false;
if (mKeyguardDelegate != null) {
- mKeyguardDelegate.onScreenTurnedOn(new KeyguardServiceDelegate.ShowListener() {
- @Override
- public void onShown(IBinder windowToken) {
- waitForKeyguardWindowDrawn(windowToken, screenOnListener);
- }
- });
+ mHandler.removeMessages(MSG_KEYGUARD_DRAWN_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_KEYGUARD_DRAWN_TIMEOUT, 1000);
+ mKeyguardDelegate.onScreenTurnedOn(mKeyguardDelegateCallback);
} else {
- Slog.i(TAG, "No keyguard interface!");
- finishScreenTurningOn(screenOnListener);
+ if (DEBUG_WAKEUP) Slog.d(TAG, "null mKeyguardDelegate: setting mKeyguardDrawComplete.");
+ mKeyguardDrawComplete = true;
}
+ mWindowManagerInternal.waitForAllWindowsDrawn(mWindowManagerDrawCallback, 500);
}
- private void waitForKeyguardWindowDrawn(IBinder windowToken,
- final ScreenOnListener screenOnListener) {
- if (windowToken != null && !mHideLockScreen) {
- try {
- if (mWindowManager.waitForWindowDrawn(
- windowToken, new IRemoteCallback.Stub() {
- @Override
- public void sendResult(Bundle data) {
- Slog.i(TAG, "Lock screen displayed!");
- finishScreenTurningOn(screenOnListener);
- setKeyguardDrawn();
- }
- })) {
- return;
- }
- Slog.i(TAG, "No lock screen! waitForWindowDrawn false");
-
- } catch (RemoteException ex) {
- // Can't happen in system process.
- }
+ // Called on the mHandler thread.
+ private void finishScreenTurningOn() {
+ if (DEBUG_WAKEUP) Slog.d(TAG,
+ "finishScreenTurningOn: mKeyguardDrawComplete=" + mKeyguardDrawComplete
+ + " mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
+ if (!mKeyguardDrawComplete || !mWindowManagerDrawComplete) {
+ return;
}
- Slog.i(TAG, "No lock screen! windowToken=" + windowToken);
- finishScreenTurningOn(screenOnListener);
- setKeyguardDrawn();
- }
-
- private void finishScreenTurningOn(ScreenOnListener screenOnListener) {
synchronized (mLock) {
mScreenOnFully = true;
}
@@ -4531,9 +4629,11 @@
} catch (RemoteException unhandled) {
}
- if (screenOnListener != null) {
- screenOnListener.onScreenOn();
+ for (int i = mScreenOnListeners.size() - 1; i >=0; --i) {
+ mScreenOnListeners.remove(i).onScreenOn();
}
+
+ setKeyguardDrawn();
}
@Override
@@ -4577,6 +4677,11 @@
return mKeyguardDelegate.isSecure();
}
+ // Returns true if keyguard is currently locked whether or not it is currently hidden.
+ private boolean isKeyguardSecureIncludingHidden() {
+ return mKeyguardDelegate.isSecure() && mKeyguardDelegate.isShowing();
+ }
+
/** {@inheritDoc} */
public boolean inKeyguardRestrictedKeyInputMode() {
if (mKeyguardDelegate == null) return false;
@@ -4886,6 +4991,7 @@
mKeyguardDelegate = new KeyguardServiceDelegate(mContext, null);
mKeyguardDelegate.onSystemReady();
+ updateUiMode();
synchronized (mLock) {
updateOrientationListenerLp();
mSystemReady = true;
@@ -4906,7 +5012,7 @@
synchronized (mLock) {
mSystemBooted = true;
}
- waitForKeyguard(null);
+ wakingUp(null);
}
ProgressDialog mBootMsgDialog = null;
@@ -5074,6 +5180,17 @@
}
}
+ void updateUiMode() {
+ if (mUiModeManager == null) {
+ mUiModeManager = IUiModeManager.Stub.asInterface(
+ ServiceManager.getService(Context.UI_MODE_SERVICE));
+ }
+ try {
+ mUiMode = mUiModeManager.getCurrentModeType();
+ } catch (RemoteException e) {
+ }
+ }
+
void updateRotation(boolean alwaysSendConfiguration) {
try {
//set orientation on WindowManager
@@ -5119,6 +5236,12 @@
if (ENABLE_DESK_DOCK_HOME_CAPTURE) {
intent = mDeskDockIntent;
}
+ } else if (mUiMode == Configuration.UI_MODE_TYPE_WATCH
+ && (mDockMode == Intent.EXTRA_DOCK_STATE_DESK
+ || mDockMode == Intent.EXTRA_DOCK_STATE_HE_DESK
+ || mDockMode == Intent.EXTRA_DOCK_STATE_LE_DESK)) {
+ // Always launch dock home from home when watch is docked, if it exists.
+ intent = mDeskDockIntent;
}
if (intent == null) {
diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java
index 723ab24..c9bba69 100644
--- a/rs/java/android/renderscript/FieldPacker.java
+++ b/rs/java/android/renderscript/FieldPacker.java
@@ -231,10 +231,24 @@
public void addObj(BaseObj obj) {
if (obj != null) {
- // FIXME: this is fine for 32-bit but needs a path for 64-bit
- addI32((int)obj.getID(null));
+ if (RenderScript.sPointerSize == 8) {
+ addI64(obj.getID(null));
+ addI64(0);
+ addI64(0);
+ addI64(0);
+ }
+ else {
+ addI32((int)obj.getID(null));
+ }
} else {
- addI32(0);
+ if (RenderScript.sPointerSize == 8) {
+ addI64(0);
+ addI64(0);
+ addI64(0);
+ addI64(0);
+ } else {
+ addI32(0);
+ }
}
}
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 2222d2c..44de480 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -64,23 +64,29 @@
static Method registerNativeFree;
/*
- * Context creation flag which specifies a normal context.
+ * Context creation flag that specifies a normal context.
*/
- public static final long CREATE_FLAG_NONE = 0x0000;
+ public static final int 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 = 0x0002;
+ public static final int CREATE_FLAG_LOW_LATENCY = 0x0002;
/*
* 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 = 0x0004;
+ public static final int CREATE_FLAG_LOW_POWER = 0x0004;
+
+ /*
+ * Detect the bitness of the VM to allow FieldPacker to do the right thing.
+ */
+ static native int rsnSystemGetPointerSize();
+ static int sPointerSize;
static {
sInitialized = false;
@@ -99,6 +105,7 @@
System.loadLibrary("rs_jni");
_nInit();
sInitialized = true;
+ sPointerSize = rsnSystemGetPointerSize();
} catch (UnsatisfiedLinkError e) {
Log.e(LOG_TAG, "Error loading RS jni library: " + e);
throw new RSRuntimeException("Error loading RS jni library: " + e);
@@ -1174,7 +1181,7 @@
* @param ctx The context.
* @return RenderScript
*/
- public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, long flags) {
+ public static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
if (!sInitialized) {
Log.e(LOG_TAG, "RenderScript.create() called when disabled; someone is likely to crash");
return null;
@@ -1187,7 +1194,7 @@
RenderScript rs = new RenderScript(ctx);
rs.mDev = rs.nDeviceCreate();
- rs.mContext = rs.nContextCreate(rs.mDev, (int)flags, sdkVersion, ct.mID);
+ rs.mContext = rs.nContextCreate(rs.mDev, flags, sdkVersion, ct.mID);
rs.mContextType = ct;
if (rs.mContext == 0) {
throw new RSDriverException("Failed to create RS context.");
@@ -1229,7 +1236,7 @@
* @param flags The OR of the CREATE_FLAG_* options desired
* @return RenderScript
*/
- public static RenderScript create(Context ctx, ContextType ct, long flags) {
+ public static RenderScript create(Context ctx, ContextType ct, int flags) {
int v = ctx.getApplicationInfo().targetSdkVersion;
return create(ctx, v, ct, flags);
}
diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java
index 9e76f52..3176e28 100644
--- a/rs/java/android/renderscript/ScriptC.java
+++ b/rs/java/android/renderscript/ScriptC.java
@@ -67,6 +67,26 @@
}
/**
+ * Only intended for use by the generated derived classes.
+ *
+ * @param rs
+ * @hide
+ */
+ protected ScriptC(RenderScript rs, String resName, byte[] bitcode32, byte[] bitcode64) {
+ super(0, rs);
+ long id = 0;
+ if (RenderScript.sPointerSize == 4) {
+ id = internalStringCreate(rs, resName, bitcode32);
+ } else {
+ id = internalStringCreate(rs, resName, bitcode64);
+ }
+ if (id == 0) {
+ throw new RSRuntimeException("Loading of ScriptC script failed.");
+ }
+ setID(id);
+ }
+
+ /**
* Name of the file that holds the object cache.
*/
private static final String CACHE_PATH = "com.android.renderscript.cache";
@@ -113,4 +133,17 @@
// Log.v(TAG, "Create script for resource = " + resName);
return rs.nScriptCCreate(resName, mCachePath, pgm, pgmLength);
}
+
+ private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) {
+ // Create the RS cache path if we haven't done so already.
+ if (mCachePath == null) {
+ File f = new File(rs.mCacheDir, CACHE_PATH);
+ mCachePath = f.getAbsolutePath();
+ f.mkdirs();
+ }
+ // Log.v(TAG, "Create script for resource = " + resName);
+ return rs.nScriptCCreate(resName, mCachePath, bitcode, bitcode.length);
+ }
+
+
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 18a2e31..ae39b05 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1573,6 +1573,12 @@
free(prims);
}
+static jint
+nSystemGetPointerSize(JNIEnv *_env, jobject _this) {
+ return (jint)sizeof(void*);
+}
+
+
// ---------------------------------------------------------------------------
@@ -1708,6 +1714,7 @@
{"rsnMeshGetVertices", "(JJ[JI)V", (void*)nMeshGetVertices },
{"rsnMeshGetIndices", "(JJ[J[II)V", (void*)nMeshGetIndices },
+{"rsnSystemGetPointerSize", "()I", (void*)nSystemGetPointerSize },
};
static int registerFuncs(JNIEnv *_env)
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index eca1bc1..1be1572 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1640,6 +1640,17 @@
pw.println("}]");
pw.println();
}
+ final int windowCount = mSecurityPolicy.mWindows.size();
+ for (int j = 0; j < windowCount; j++) {
+ if (j > 0) {
+ pw.append(',');
+ pw.println();
+ }
+ pw.append("Window[");
+ AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(j);
+ pw.append(window.toString());
+ pw.append(']');
+ }
}
}
@@ -3283,7 +3294,7 @@
}
if (mTouchInteractionInProgress && activeWindowGone) {
- mActiveWindowId = INVALID_WINDOW_ID;
+ mActiveWindowId = mFocusedWindowId;
}
// Focused window may change the active one, so set the
@@ -3291,7 +3302,7 @@
for (int i = 0; i < windowCount; i++) {
AccessibilityWindowInfo window = mWindows.get(i);
if (window.getId() == mActiveWindowId) {
- window.setActive(true);
+ window.setActive(true);
}
}
}
@@ -3336,10 +3347,11 @@
// The active window also determined events from which
// windows are delivered.
synchronized (mLock) {
- mFocusedWindowId = getFocusedWindowId();
- if (mWindowsForAccessibilityCallback == null
- && windowId == mFocusedWindowId) {
- mActiveWindowId = windowId;
+ if (mWindowsForAccessibilityCallback == null) {
+ mFocusedWindowId = getFocusedWindowId();
+ if (windowId == mFocusedWindowId) {
+ mActiveWindowId = windowId;
+ }
}
}
} break;
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index cb5946a..aeb195f 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.database.ContentObserver;
import android.os.BatteryStats;
import com.android.internal.app.IBatteryStats;
import com.android.server.am.BatteryStatsService;
@@ -128,6 +129,8 @@
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
+ private boolean mBatteryLevelLow;
+
private long mDischargeStartTime;
private int mDischargeStartLevel;
@@ -147,8 +150,8 @@
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mLowBatteryWarningLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
- mLowBatteryCloseWarningLevel = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
+ mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);
@@ -171,11 +174,39 @@
void systemReady() {
// check our power situation now that it is safe to display the shutdown dialog.
synchronized (mLock) {
- shutdownIfNoPowerLocked();
- shutdownIfOverTempLocked();
+ ContentObserver obs = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateBatteryWarningLevelLocked();
+ }
+ }
+ };
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+ false, obs, UserHandle.USER_ALL);
+ updateBatteryWarningLevelLocked();
}
}
+ void updateBatteryWarningLevelLocked() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ int defWarnLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryWarningLevel);
+ mLowBatteryWarningLevel = Settings.Global.getInt(resolver,
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
+ if (mLowBatteryWarningLevel == 0) {
+ mLowBatteryWarningLevel = defWarnLevel;
+ }
+ if (mLowBatteryWarningLevel < mCriticalBatteryLevel) {
+ mLowBatteryWarningLevel = mCriticalBatteryLevel;
+ }
+ mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
+ processValuesLocked(true);
+ }
+
/**
* Returns true if the device is plugged into any of the specified plug types.
*/
@@ -222,14 +253,30 @@
}
/**
- * Returns true if battery level is below the first warning threshold.
+ * Returns whether we currently consider the battery level to be low.
*/
- public boolean isBatteryLow() {
+ public boolean getBatteryLevelLow() {
synchronized (mLock) {
- return mBatteryProps.batteryPresent && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel;
+ return mBatteryLevelLow;
}
}
+ public boolean shouldSendBatteryLowLocked() {
+ final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
+ final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
+
+ /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
+ * - is just un-plugged (previously was plugged) and battery level is
+ * less than or equal to WARNING, or
+ * - is not plugged and battery level falls to WARNING boundary
+ * (becomes <= mLowBatteryWarningLevel).
+ */
+ return !plugged
+ && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
+ && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+ }
+
/**
* Returns a non-zero value if an unsupported charger is attached.
*/
@@ -281,14 +328,14 @@
if (!mUpdatesStopped) {
mBatteryProps = props;
// Process the new values.
- processValuesLocked();
+ processValuesLocked(false);
} else {
mLastBatteryProps.set(props);
}
}
}
- private void processValuesLocked() {
+ private void processValuesLocked(boolean force) {
boolean logOutlier = false;
long dischargeDuration = 0;
@@ -331,14 +378,14 @@
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
- if (mBatteryProps.batteryStatus != mLastBatteryStatus ||
+ if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus ||
mBatteryProps.batteryHealth != mLastBatteryHealth ||
mBatteryProps.batteryPresent != mLastBatteryPresent ||
mBatteryProps.batteryLevel != mLastBatteryLevel ||
mPlugType != mLastPlugType ||
mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
- mInvalidCharger != mLastInvalidCharger) {
+ mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -382,19 +429,24 @@
logOutlier = true;
}
- final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
- final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
-
- /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
- * - is just un-plugged (previously was plugged) and battery level is
- * less than or equal to WARNING, or
- * - is not plugged and battery level falls to WARNING boundary
- * (becomes <= mLowBatteryWarningLevel).
- */
- final boolean sendBatteryLow = !plugged
- && mBatteryProps.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
- && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel
- && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
+ if (!mBatteryLevelLow) {
+ // Should we now switch in to low battery mode?
+ if (mPlugType == BATTERY_PLUGGED_NONE
+ && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) {
+ mBatteryLevelLow = true;
+ }
+ } else {
+ // Should we now switch out of low battery mode?
+ if (mPlugType != BATTERY_PLUGGED_NONE) {
+ mBatteryLevelLow = false;
+ } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) {
+ mBatteryLevelLow = false;
+ } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) {
+ // If being forced, the previous state doesn't matter, we will just
+ // absolutely check to see if we are now above the warning level.
+ mBatteryLevelLow = false;
+ }
+ }
sendIntentLocked();
@@ -422,7 +474,7 @@
});
}
- if (sendBatteryLow) {
+ if (shouldSendBatteryLowLocked()) {
mSentLowBatteryBroadcast = true;
mHandler.post(new Runnable() {
@Override
@@ -644,7 +696,7 @@
long ident = Binder.clearCallingIdentity();
try {
mUpdatesStopped = true;
- processValuesLocked();
+ processValuesLocked(false);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -658,7 +710,7 @@
if (mUpdatesStopped) {
mUpdatesStopped = false;
mBatteryProps.set(mLastBatteryProps);
- processValuesLocked();
+ processValuesLocked(false);
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b2b4217..00bda06 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -20,7 +20,6 @@
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
-import static android.net.ConnectivityManager.NetworkCallbackListener;
import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
import static android.net.ConnectivityManager.TYPE_DUMMY;
import static android.net.ConnectivityManager.TYPE_MOBILE;
@@ -630,8 +629,8 @@
if (DBG) log("ConnectivityService starting up");
NetworkCapabilities netCap = new NetworkCapabilities();
- netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(),
NetworkRequestInfo.REQUEST);
@@ -1669,7 +1668,7 @@
continue;
}
- int prefix = destination.getNetworkPrefixLength();
+ int prefix = destination.getPrefixLength();
InetAddress addrMasked = NetworkUtils.getNetworkPart(address, prefix);
InetAddress destMasked = NetworkUtils.getNetworkPart(destination.getAddress(),
prefix);
@@ -1754,31 +1753,34 @@
if (DBG) log("requestRouteToHostAddress on invalid network: " + networkType);
return false;
}
- NetworkStateTracker tracker = mNetTrackers[networkType];
- DetailedState netState = DetailedState.DISCONNECTED;
- if (tracker != null) {
- netState = tracker.getNetworkInfo().getDetailedState();
+
+ NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+ if (nai == null) {
+ if (mLegacyTypeTracker.isTypeSupported(networkType) == false) {
+ if (DBG) log("requestRouteToHostAddress on unsupported network: " + networkType);
+ } else {
+ if (DBG) log("requestRouteToHostAddress on down network: " + networkType);
+ }
+ return false;
}
+ DetailedState netState = nai.networkInfo.getDetailedState();
+
if ((netState != DetailedState.CONNECTED &&
- netState != DetailedState.CAPTIVE_PORTAL_CHECK) ||
- tracker.isTeardownRequested()) {
+ netState != DetailedState.CAPTIVE_PORTAL_CHECK)) {
if (VDBG) {
log("requestRouteToHostAddress on down network "
+ "(" + networkType + ") - dropped"
- + " tracker=" + tracker
- + " netState=" + netState
- + " isTeardownRequested="
- + ((tracker != null) ? tracker.isTeardownRequested() : "tracker:null"));
+ + " netState=" + netState);
}
return false;
}
final int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- LinkProperties lp = tracker.getLinkProperties();
+ LinkProperties lp = nai.linkProperties;
boolean ok = modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt,
- tracker.getNetwork().netId, uid);
+ nai.network.netId, uid);
if (DBG) log("requestRouteToHostAddress ok=" + ok);
return ok;
} finally {
@@ -1868,7 +1870,7 @@
mNetd.addRoute(netId, r);
}
if (exempt) {
- LinkAddress dest = r.getDestination();
+ LinkAddress dest = r.getDestinationLinkAddress();
if (!mExemptAddresses.contains(dest)) {
mNetd.setHostExemption(dest);
mExemptAddresses.add(dest);
@@ -1901,7 +1903,7 @@
} else {
mNetd.removeRoute(netId, r);
}
- LinkAddress dest = r.getDestination();
+ LinkAddress dest = r.getDestinationLinkAddress();
if (mExemptAddresses.contains(dest)) {
mNetd.clearHostExemption(dest);
mExemptAddresses.remove(dest);
@@ -2688,7 +2690,7 @@
dnsDiff = curLp.compareDnses(newLp);
} else if (newLp != null) {
routeDiff.added = newLp.getAllRoutes();
- dnsDiff.added = newLp.getDnses();
+ dnsDiff.added = newLp.getDnsServers();
}
boolean routesChanged = (routeDiff.removed.size() != 0 || routeDiff.added.size() != 0);
@@ -2912,7 +2914,7 @@
if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
LinkProperties p = nt.getLinkProperties();
if (p == null) return;
- Collection<InetAddress> dnses = p.getDnses();
+ Collection<InetAddress> dnses = p.getDnsServers();
int netId = nt.getNetwork().netId;
if (mNetConfigs[netType].isDefault()) {
String network = nt.getNetworkInfo().getTypeName();
@@ -3100,7 +3102,7 @@
break;
}
Integer score = (Integer) msg.obj;
- updateNetworkScore(nai, score);
+ if (score != null) updateNetworkScore(nai, score.intValue());
break;
}
case NetworkMonitor.EVENT_NETWORK_VALIDATED: {
@@ -3316,6 +3318,7 @@
if (bestNetwork != null) {
if (VDBG) log("using " + bestNetwork.name());
bestNetwork.addRequest(nri.request);
+ mNetworkForRequestId.put(nri.request.requestId, bestNetwork);
int legacyType = nri.request.legacyType;
if (legacyType != TYPE_NONE) {
mLegacyTypeTracker.add(legacyType, bestNetwork);
@@ -4434,7 +4437,9 @@
mIsProvisioningNetwork.set(true);
MobileDataStateTracker mdst = (MobileDataStateTracker)
mNetTrackers[ConnectivityManager.TYPE_MOBILE];
- mdst.setInternalDataEnable(false);
+
+ // Disable radio until user starts provisioning
+ mdst.setRadio(false);
} else {
if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
}
@@ -4946,17 +4951,24 @@
// Mark notification as not visible
setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null);
- // If provisioning network handle as a special case,
+ // Check airplane mode
+ boolean isAirplaneModeOn = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+ // If provisioning network and not in airplane mode handle as a special case,
// otherwise launch browser with the intent directly.
- if (mIsProvisioningNetwork.get()) {
+ if (mIsProvisioningNetwork.get() && !isAirplaneModeOn) {
if (DBG) log("handleMobileProvisioningAction: on prov network enable then launch");
+ mIsProvisioningNetwork.set(false);
// mIsStartingProvisioning.set(true);
// MobileDataStateTracker mdst = (MobileDataStateTracker)
// mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+ // Radio was disabled on CMP_RESULT_CODE_PROVISIONING_NETWORK, enable it here
+// mdst.setRadio(true);
// mdst.setEnableFailFastMobileData(DctConstants.ENABLED);
// mdst.enableMobileProvisioning(url);
} else {
if (DBG) log("handleMobileProvisioningAction: not prov network");
+ mIsProvisioningNetwork.set(false);
// Check for apps that can handle provisioning first
Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
@@ -5397,7 +5409,7 @@
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, int timeoutSec, IBinder binder, int legacyType) {
+ Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
== false) {
enforceConnectivityInternalPermission();
@@ -5405,7 +5417,7 @@
enforceChangePermission();
}
- if (timeoutSec < 0 || timeoutSec > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_SEC) {
+ if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
throw new IllegalArgumentException("Bad timeout specified");
}
NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
@@ -5415,9 +5427,9 @@
NetworkRequestInfo.REQUEST);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
- if (timeoutSec > 0) {
+ if (timeoutMs > 0) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST,
- nri), timeoutSec * 1000);
+ nri), timeoutMs);
}
return networkRequest;
}
@@ -5621,7 +5633,7 @@
}
private void updateDnses(LinkProperties newLp, LinkProperties oldLp, int netId) {
if (oldLp == null || (newLp.isIdenticalDnses(oldLp) == false)) {
- Collection<InetAddress> dnses = newLp.getDnses();
+ Collection<InetAddress> dnses = newLp.getDnsServers();
if (dnses.size() == 0 && mDefaultDns != null) {
dnses = new ArrayList();
dnses.add(mDefaultDns);
@@ -5681,7 +5693,7 @@
int a2 = 0;
switch (notificationType) {
case ConnectivityManager.CALLBACK_LOSING:
- a1 = 30; // TODO - read this from NetworkMonitor
+ a1 = 30 * 1000; // TODO - read this from NetworkMonitor
// fall through
case ConnectivityManager.CALLBACK_PRECHECK:
case ConnectivityManager.CALLBACK_AVAILABLE:
@@ -5786,7 +5798,8 @@
isNewDefault = true;
updateActiveDefaultNetwork(newNetwork);
if (newNetwork.linkProperties != null) {
- setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnses());
+ setDefaultDnsSystemProperties(
+ newNetwork.linkProperties.getDnsServers());
} else {
setDefaultDnsSystemProperties(new ArrayList<InetAddress>());
}
@@ -5911,9 +5924,30 @@
}
}
- private void updateNetworkScore(NetworkAgentInfo nai, Integer scoreInteger) {
- int score = scoreInteger.intValue();
- // TODO
+ private void updateNetworkScore(NetworkAgentInfo nai, int score) {
+ if (DBG) log("updateNetworkScore for " + nai.name() + " to " + score);
+
+ nai.currentScore = score;
+
+ // TODO - This will not do the right thing if this network is lowering
+ // its score and has requests that can be served by other
+ // currently-active networks, or if the network is increasing its
+ // score and other networks have requests that can be better served
+ // by this network.
+ //
+ // Really we want to see if any of our requests migrate to other
+ // active/lingered networks and if any other requests migrate to us (depending
+ // on increasing/decreasing currentScore. That's a bit of work and probably our
+ // score checking/network allocation code needs to be modularized so we can understand
+ // (see handleConnectionValided for an example).
+ //
+ // As a first order approx, lets just advertise the new score to factories. If
+ // somebody can beat it they will nominate a network and our normal net replacement
+ // code will fire.
+ for (int i = 0; i < nai.networkRequests.size(); i++) {
+ NetworkRequest nr = nai.networkRequests.valueAt(i);
+ sendUpdatedScoreToFactories(nr, score);
+ }
}
// notify only this one new request of the current state
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 3a8f767..8fb2e9f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -149,6 +149,7 @@
static final int MSG_BIND_METHOD = 3010;
static final int MSG_SET_ACTIVE = 3020;
static final int MSG_SET_CURSOR_ANCHOR_MONITOR_MODE = 3030;
+ static final int MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER = 3040;
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
@@ -172,7 +173,7 @@
private final HardKeyboardListener mHardKeyboardListener;
private final WindowManagerService mWindowManagerService;
- final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1);
+ final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1, -1);
// All known input methods. mMethodMap also serves as the global
// lock for this class.
@@ -379,6 +380,8 @@
*/
boolean mScreenOn = true;
+ int mCurUserActionNotificationSequenceNumber = 0;
+
int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
int mImeWindowVis;
@@ -961,6 +964,19 @@
return false;
}
+
+ /**
+ * Returns true iff the caller is identified to be the current input method with the token.
+ * @param token The window token given to the input method when it was started.
+ * @return true if and only if non-null valid token is specified.
+ */
+ private boolean calledWithValidToken(IBinder token) {
+ if (token == null || mCurToken != token) {
+ return false;
+ }
+ return true;
+ }
+
private boolean bindCurrentInputMethodService(
Intent service, ServiceConnection conn, int flags) {
if (service == null || conn == null) {
@@ -1116,7 +1132,8 @@
showCurrentInputLocked(getAppShowFlags(), null);
}
return new InputBindResult(session.session,
- session.channel != null ? session.channel.dup() : null, mCurId, mCurSeq);
+ (session.channel != null ? session.channel.dup() : null),
+ mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
InputBindResult startInputLocked(IInputMethodClient client,
@@ -1192,7 +1209,8 @@
// Return to client, and we will get back with it when
// we have had a session made for it.
requestClientSessionLocked(cs);
- return new InputBindResult(null, null, mCurId, mCurSeq);
+ return new InputBindResult(null, null, mCurId, mCurSeq,
+ mCurUserActionNotificationSequenceNumber);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
// In this case we have connected to the service, but
@@ -1202,7 +1220,8 @@
// we can report back. If it has been too long, we want
// to fall through so we can try a disconnect/reconnect
// to see if we can get back in touch with the service.
- return new InputBindResult(null, null, mCurId, mCurSeq);
+ return new InputBindResult(null, null, mCurId, mCurSeq,
+ mCurUserActionNotificationSequenceNumber);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -1221,7 +1240,8 @@
if (!mSystemReady) {
// If the system is not yet ready, we shouldn't be running third
// party code.
- return new InputBindResult(null, null, mCurMethodId, mCurSeq);
+ return new InputBindResult(null, null, mCurMethodId, mCurSeq,
+ mCurUserActionNotificationSequenceNumber);
}
InputMethodInfo info = mMethodMap.get(mCurMethodId);
@@ -1250,7 +1270,8 @@
WindowManager.LayoutParams.TYPE_INPUT_METHOD);
} catch (RemoteException e) {
}
- return new InputBindResult(null, null, mCurId, mCurSeq);
+ return new InputBindResult(null, null, mCurId, mCurSeq,
+ mCurUserActionNotificationSequenceNumber);
} else {
mCurIntent = null;
Slog.w(TAG, "Failure connecting to input method service: "
@@ -1429,15 +1450,15 @@
@Override
public void updateStatusIcon(IBinder token, String packageName, int iconId) {
- int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
try {
- if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring setInputMethod of uid " + uid + " token: " + token);
- return;
- }
-
synchronized (mMethodMap) {
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring updateStatusIcon due to an invalid token. uid:" + uid
+ + " token:" + token);
+ return;
+ }
if (iconId == 0) {
if (DEBUG) Slog.d(TAG, "hide the small icon for the input method");
if (mStatusBar != null) {
@@ -1527,9 +1548,10 @@
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
final long ident = Binder.clearCallingIdentity();
try {
- if (token == null || mCurToken != token) {
- int uid = Binder.getCallingUid();
- Slog.w(TAG, "Ignoring setImeWindowStatus of uid " + uid + " token: " + token);
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring setImeWindowStatus due to an invalid token. uid:" + uid
+ + " token:" + token);
return;
}
synchronized (mMethodMap) {
@@ -2085,8 +2107,9 @@
}
synchronized (mMethodMap) {
if (subtype != null) {
- setInputMethodWithSubtypeId(token, id, InputMethodUtils.getSubtypeIdFromHashCode(
- mMethodMap.get(id), subtype.hashCode()));
+ setInputMethodWithSubtypeIdLocked(token, id,
+ InputMethodUtils.getSubtypeIdFromHashCode(mMethodMap.get(id),
+ subtype.hashCode()));
} else {
setInputMethod(token, id);
}
@@ -2173,7 +2196,7 @@
Slog.d(TAG, "Switch to: " + lastImi.getId() + ", " + lastIme.second
+ ", from: " + mCurMethodId + ", " + subtypeId);
}
- setInputMethodWithSubtypeId(token, targetLastImiId, subtypeId);
+ setInputMethodWithSubtypeIdLocked(token, targetLastImiId, subtypeId);
return true;
} else {
return false;
@@ -2187,12 +2210,19 @@
return false;
}
synchronized (mMethodMap) {
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring switchToNextInputMethod due to an invalid token. uid:" + uid
+ + " token:" + token);
+ return false;
+ }
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype);
if (nextSubtype == null) {
return false;
}
- setInputMethodWithSubtypeId(token, nextSubtype.mImi.getId(), nextSubtype.mSubtypeId);
+ setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(),
+ nextSubtype.mSubtypeId);
return true;
}
}
@@ -2203,6 +2233,12 @@
return false;
}
synchronized (mMethodMap) {
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring shouldOfferSwitchingToNextInputMethod due to an invalid "
+ + "token. uid:" + uid + " token:" + token);
+ return false;
+ }
final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked(
false /* onlyCurrentIme */, mMethodMap.get(mCurMethodId), mCurrentSubtype);
if (nextSubtype == null) {
@@ -2282,14 +2318,22 @@
}
@Override
- public void notifyTextCommitted() {
+ public void notifyUserAction(int sequenceNumber) {
if (DEBUG) {
- Slog.d(TAG, "Got the notification of commitText");
+ Slog.d(TAG, "Got the notification of a user action. sequenceNumber:" + sequenceNumber);
}
synchronized (mMethodMap) {
+ if (mCurUserActionNotificationSequenceNumber != sequenceNumber) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring the user action notification due to the sequence number "
+ + "mismatch. expected:" + mCurUserActionNotificationSequenceNumber
+ + " actual: " + sequenceNumber);
+ }
+ return;
+ }
final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
if (imi != null) {
- mSwitchingController.onCommitTextLocked(imi, mCurrentSubtype);
+ mSwitchingController.onUserActionLocked(imi, mCurrentSubtype);
}
}
}
@@ -2303,11 +2347,10 @@
return;
}
synchronized (mMethodMap) {
- if (token == null || mCurToken != token) {
- if (DEBUG) {
- Slog.w(TAG, "Ignoring setCursorAnchorMonitorMode from uid "
- + Binder.getCallingUid() + " token: " + token);
- }
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring setCursorAnchorMonitorMode due to an invalid token. uid:"
+ + uid + " token:" + token);
return;
}
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO(
@@ -2317,26 +2360,30 @@
private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) {
synchronized (mMethodMap) {
- if (token == null) {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.WRITE_SECURE_SETTINGS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException(
- "Using null token requires permission "
- + android.Manifest.permission.WRITE_SECURE_SETTINGS);
- }
- } else if (mCurToken != token) {
- Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
- + " token: " + token);
- return;
- }
+ setInputMethodWithSubtypeIdLocked(token, id, subtypeId);
+ }
+ }
- final long ident = Binder.clearCallingIdentity();
- try {
- setInputMethodLocked(id, subtypeId);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
+ if (token == null) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Using null token requires permission "
+ + android.Manifest.permission.WRITE_SECURE_SETTINGS);
}
+ } else if (mCurToken != token) {
+ Slog.w(TAG, "Ignoring setInputMethod of uid " + Binder.getCallingUid()
+ + " token: " + token);
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setInputMethodLocked(id, subtypeId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -2346,9 +2393,10 @@
return;
}
synchronized (mMethodMap) {
- if (token == null || mCurToken != token) {
- if (DEBUG) Slog.w(TAG, "Ignoring hideInputMethod of uid "
- + Binder.getCallingUid() + " token: " + token);
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring hideInputMethod due to an invalid token. uid:"
+ + uid + " token:" + token);
return;
}
long ident = Binder.clearCallingIdentity();
@@ -2366,9 +2414,10 @@
return;
}
synchronized (mMethodMap) {
- if (token == null || mCurToken != token) {
- Slog.w(TAG, "Ignoring showMySoftInput of uid "
- + Binder.getCallingUid() + " token: " + token);
+ if (!calledWithValidToken(token)) {
+ final int uid = Binder.getCallingUid();
+ Slog.e(TAG, "Ignoring showMySoftInput due to an invalid token. uid:"
+ + uid + " token:" + token);
return;
}
long ident = Binder.clearCallingIdentity();
@@ -2555,6 +2604,20 @@
+ " uid " + ((ClientState)msg.obj).uid);
}
return true;
+ case MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER: {
+ final int sequenceNumber = msg.arg1;
+ final IInputMethodClient client = (IInputMethodClient)msg.obj;
+ try {
+ client.setUserActionNotificationSequenceNumber(sequenceNumber);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Got RemoteException sending "
+ + "setUserActionNotificationSequenceNumber("
+ + sequenceNumber + ") notification to pid "
+ + ((ClientState)msg.obj).pid + " uid "
+ + ((ClientState)msg.obj).uid);
+ }
+ return true;
+ }
// --------------------------------------------------------------
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
@@ -2966,6 +3029,19 @@
// Update the history of InputMethod and Subtype
mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
+ mCurUserActionNotificationSequenceNumber =
+ Math.max(mCurUserActionNotificationSequenceNumber + 1, 1);
+ if (DEBUG) {
+ Slog.d(TAG, "Bump mCurUserActionNotificationSequenceNumber:"
+ + mCurUserActionNotificationSequenceNumber);
+ }
+
+ if (mCurClient != null && mCurClient.client != null) {
+ executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
+ MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
+ mCurUserActionNotificationSequenceNumber, mCurClient.client));
+ }
+
// Set Subtype here
if (imi == null || subtypeId < 0) {
mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
@@ -3457,6 +3533,8 @@
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
+ " mShowForced=" + mShowForced
+ " mInputShown=" + mInputShown);
+ p.println(" mCurUserActionNotificationSequenceNumber="
+ + mCurUserActionNotificationSequenceNumber);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn);
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 11fc941..10382ba 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -29,6 +29,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.location.Address;
@@ -55,10 +56,12 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
+
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
@@ -144,6 +147,7 @@
private GeofenceManager mGeofenceManager;
private PackageManager mPackageManager;
private PowerManager mPowerManager;
+ private UserManager mUserManager;
private GeocoderProxy mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
@@ -197,6 +201,7 @@
// current active user on the device - other users are denied location data
private int mCurrentUserId = UserHandle.USER_OWNER;
+ private int[] mCurrentUserProfiles = new int[] { UserHandle.USER_OWNER };
public LocationManagerService(Context context) {
super();
@@ -241,6 +246,9 @@
};
mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null, callback);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ updateUserProfiles(mCurrentUserId);
+
// prepare providers
loadProvidersLocked();
updateProvidersLocked();
@@ -262,6 +270,8 @@
// listen for user change
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+ intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
@@ -269,11 +279,46 @@
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
+ || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+ updateUserProfiles(mCurrentUserId);
}
}
}, UserHandle.ALL, intentFilter, null, mLocationHandler);
}
+ /**
+ * Makes a list of userids that are related to the current user. This is
+ * relevant when using managed profiles. Otherwise the list only contains
+ * the current user.
+ *
+ * @param currentUserId the current user, who might have an alter-ego.
+ */
+ void updateUserProfiles(int currentUserId) {
+ List<UserInfo> profiles = mUserManager.getProfiles(currentUserId);
+ synchronized (mLock) {
+ mCurrentUserProfiles = new int[profiles.size()];
+ for (int i = 0; i < mCurrentUserProfiles.length; i++) {
+ mCurrentUserProfiles[i] = profiles.get(i).id;
+ }
+ }
+ }
+
+ /**
+ * Checks if the specified userId matches any of the current foreground
+ * users stored in mCurrentUserProfiles.
+ */
+ private boolean isCurrentProfile(int userId) {
+ synchronized (mLock) {
+ for (int i = 0; i < mCurrentUserProfiles.length; i++) {
+ if (mCurrentUserProfiles[i] == userId) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
private void ensureFallbackFusedProviderPresentLocked(ArrayList<String> pkgs) {
PackageManager pm = mContext.getPackageManager();
String systemPackageName = mContext.getPackageName();
@@ -427,14 +472,15 @@
}
// bind to fused provider if supported
- if (FlpHardwareProvider.getInstance(mContext).isSupported()) {
- FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
+ if (FlpHardwareProvider.isSupported()) {
+ FlpHardwareProvider flpHardwareProvider =
+ FlpHardwareProvider.getInstance(mContext);
FusedProxy fusedProxy = FusedProxy.createAndBind(
mContext,
mLocationHandler,
flpHardwareProvider.getLocationHardware(),
- com.android.internal.R.bool.config_enableFusedLocationOverlay,
- com.android.internal.R.string.config_fusedLocationProviderPackageName,
+ com.android.internal.R.bool.config_enableHardwareFlpOverlay,
+ com.android.internal.R.string.config_hardwareFlpPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if(fusedProxy == null) {
Slog.e(TAG, "Unable to bind FusedProxy.");
@@ -491,9 +537,10 @@
mLastLocation.clear();
mLastLocationCoarseInterval.clear();
for (LocationProviderInterface p : mProviders) {
- updateProviderListenersLocked(p.getName(), false, mCurrentUserId);
+ updateProviderListenersLocked(p.getName(), false);
}
mCurrentUserId = userId;
+ updateUserProfiles(userId);
updateProvidersLocked();
}
}
@@ -893,7 +940,7 @@
* @return
*/
private boolean isAllowedByUserSettingsLocked(String provider, int uid) {
- if (UserHandle.getUserId(uid) != mCurrentUserId && !isUidALocationProvider(uid)) {
+ if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
return false;
}
return isAllowedByCurrentUserSettingsLocked(provider);
@@ -1180,7 +1227,7 @@
String name = p.getName();
boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
if (isEnabled && !shouldBeEnabled) {
- updateProviderListenersLocked(name, false, mCurrentUserId);
+ updateProviderListenersLocked(name, false);
// If any provider has been disabled, clear all last locations for all providers.
// This is to be on the safe side in case a provider has location derived from
// this disabled provider.
@@ -1188,7 +1235,7 @@
mLastLocationCoarseInterval.clear();
changesMade = true;
} else if (!isEnabled && shouldBeEnabled) {
- updateProviderListenersLocked(name, true, mCurrentUserId);
+ updateProviderListenersLocked(name, true);
changesMade = true;
}
}
@@ -1200,7 +1247,7 @@
}
}
- private void updateProviderListenersLocked(String provider, boolean enabled, int userId) {
+ private void updateProviderListenersLocked(String provider, boolean enabled) {
int listeners = 0;
LocationProviderInterface p = mProvidersByName.get(provider);
@@ -1213,7 +1260,7 @@
final int N = records.size();
for (int i = 0; i < N; i++) {
UpdateRecord record = records.get(i);
- if (UserHandle.getUserId(record.mReceiver.mUid) == userId) {
+ if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
// Sends a notification message to the receiver
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
if (deadReceivers == null) {
@@ -1252,7 +1299,7 @@
if (records != null) {
for (UpdateRecord record : records) {
- if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+ if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
if (checkLocationAccess(record.mReceiver.mUid, record.mReceiver.mPackageName,
record.mReceiver.mAllowedResolutionLevel)) {
LocationRequest locationRequest = record.mRequest;
@@ -1273,7 +1320,7 @@
// under that threshold.
long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
for (UpdateRecord record : records) {
- if (UserHandle.getUserId(record.mReceiver.mUid) == mCurrentUserId) {
+ if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mUid))) {
LocationRequest locationRequest = record.mRequest;
if (locationRequest.getInterval() <= thresholdInterval) {
if (record.mReceiver.mWorkSource != null
@@ -2017,7 +2064,7 @@
boolean receiverDead = false;
int receiverUserId = UserHandle.getUserId(receiver.mUid);
- if (receiverUserId != mCurrentUserId && !isUidALocationProvider(receiver.mUid)) {
+ if (!isCurrentProfile(receiverUserId) && !isUidALocationProvider(receiver.mUid)) {
if (D) {
Log.d(TAG, "skipping loc update for background user " + receiverUserId +
" (current user: " + mCurrentUserId + ", app: " +
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index d31fb60..39410c2 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -629,6 +629,11 @@
sendUmsIntent(true);
mSendUmsConnectedOnBoot = false;
}
+
+ /*
+ * Start scheduling nominally-daily fstrim operations
+ */
+ MountServiceIdler.scheduleIdlePass(mContext);
}
private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index 8b19321..bcb6e9e 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -16,34 +16,94 @@
package com.android.server;
-import android.app.maintenance.IdleService;
+import java.util.Calendar;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
import android.util.Slog;
-public class MountServiceIdler extends IdleService {
+public class MountServiceIdler extends JobService {
private static final String TAG = "MountServiceIdler";
+ private static ComponentName sIdleService =
+ new ComponentName(MountServiceIdler.class.getPackage().getName(),
+ MountServiceIdler.class.getName());
+
+ private static int MOUNT_JOB_ID = 808;
+
+ private boolean mStarted;
+ private JobParameters mJobParams;
private Runnable mFinishCallback = new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Got mount service completion callback");
- finishIdle();
+ synchronized (mFinishCallback) {
+ if (mStarted) {
+ jobFinished(mJobParams, false);
+ mStarted = false;
+ }
+ }
+ // ... and try again tomorrow
+ scheduleIdlePass(MountServiceIdler.this);
}
};
@Override
- public boolean onIdleStart() {
+ public boolean onStartJob(JobParameters params) {
// The mount service will run an fstrim operation asynchronously
// on a designated separate thread, so we provide it with a callback
// that lets us cleanly end our idle timeslice. It's safe to call
// finishIdle() from any thread.
+ mJobParams = params;
MountService ms = MountService.sSelf;
if (ms != null) {
+ synchronized (mFinishCallback) {
+ mStarted = true;
+ }
ms.runIdleMaintenance(mFinishCallback);
}
return ms != null;
}
@Override
- public void onIdleStop() {
+ public boolean onStopJob(JobParameters params) {
+ // Once we kick off the fstrim we aren't actually interruptible; just note
+ // that we don't need to call jobFinished(), and let everything happen in
+ // the callback from the mount service.
+ synchronized (mFinishCallback) {
+ mStarted = false;
+ }
+ return false;
+ }
+
+ /**
+ * Schedule the idle job that will ping the mount service
+ */
+ public static void scheduleIdlePass(Context context) {
+ JobScheduler tm = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+ Calendar calendar = tomorrowMidnight();
+ final long timeToMidnight = calendar.getTimeInMillis() - System.currentTimeMillis();
+
+ JobInfo.Builder builder = new JobInfo.Builder(MOUNT_JOB_ID, sIdleService);
+ builder.setRequiresDeviceIdle(true);
+ builder.setRequiresCharging(true);
+ builder.setMinimumLatency(timeToMidnight);
+ tm.schedule(builder.build());
+ }
+
+ private static Calendar tomorrowMidnight() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(System.currentTimeMillis());
+ calendar.set(Calendar.HOUR, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ return calendar;
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index eefe8da..362061e 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -795,7 +795,7 @@
final Command cmd = new Command("interface", "setcfg", iface,
linkAddr.getAddress().getHostAddress(),
- linkAddr.getNetworkPrefixLength());
+ linkAddr.getPrefixLength());
for (String flag : cfg.getFlags()) {
cmd.appendArg(flag);
}
@@ -882,9 +882,9 @@
final Command cmd = new Command("network", "route", action, netId);
// create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
- final LinkAddress la = route.getDestination();
+ final LinkAddress la = route.getDestinationLinkAddress();
cmd.appendArg(route.getInterface());
- cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getNetworkPrefixLength());
+ cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getPrefixLength());
if (route.hasGateway()) {
cmd.appendArg(route.getGateway().getHostAddress());
}
@@ -1697,9 +1697,9 @@
public void setMarkedForwardingRoute(String iface, RouteInfo route) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- LinkAddress dest = route.getDestination();
+ LinkAddress dest = route.getDestinationLinkAddress();
mConnector.execute("interface", "fwmark", "route", "add", iface,
- dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength());
+ dest.getAddress().getHostAddress(), dest.getPrefixLength());
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
@@ -1709,9 +1709,9 @@
public void clearMarkedForwardingRoute(String iface, RouteInfo route) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- LinkAddress dest = route.getDestination();
+ LinkAddress dest = route.getDestinationLinkAddress();
mConnector.execute("interface", "fwmark", "route", "remove", iface,
- dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength());
+ dest.getAddress().getHostAddress(), dest.getPrefixLength());
} catch (NativeDaemonConnectorException e) {
throw e.rethrowAsParcelableException();
}
@@ -1998,9 +1998,9 @@
final Command cmd = new Command("network", "route", "legacy", uid, action, netId);
// create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
- final LinkAddress la = routeInfo.getDestination();
+ final LinkAddress la = routeInfo.getDestinationLinkAddress();
cmd.appendArg(routeInfo.getInterface());
- cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getNetworkPrefixLength());
+ cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getPrefixLength());
if (routeInfo.hasGateway()) {
cmd.appendArg(routeInfo.getGateway().getHostAddress());
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 06dd3ed..fdaf55e 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -28,6 +28,7 @@
import android.os.Handler;
import android.os.IVibratorService;
import android.os.PowerManager;
+import android.os.PowerManagerInternal;
import android.os.Process;
import android.os.RemoteException;
import android.os.IBinder;
@@ -64,6 +65,7 @@
private final PowerManager.WakeLock mWakeLock;
private final IAppOpsService mAppOpsService;
private final IBatteryStats mBatteryStatsService;
+ private PowerManagerInternal mPowerManagerInternal;
private InputManager mIm;
volatile VibrateThread mThread;
@@ -169,14 +171,19 @@
mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
mSettingObserver = new SettingsObserver(mH);
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ new PowerManagerInternal.LowPowerModeListener() {
+ @Override
+ public void onLowPowerModeChanged(boolean enabled) {
+ updateInputDeviceVibrators();
+ }
+ });
+
mContext.getContentResolver().registerContentObserver(
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
public void onReceive(Context context, Intent intent) {
@@ -448,8 +455,7 @@
} catch (SettingNotFoundException snfe) {
}
- mLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.LOW_POWER_MODE, 0) != 0;
+ mLowPowerMode = mPowerManagerInternal.getLowPowerModeEnabled();
if (mVibrateInputDevicesSetting) {
if (!mInputDeviceListenerRegistered) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index b2aaf74..e152ebe 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -537,13 +537,16 @@
}
@Override
- public AuthenticatorDescription[] getAuthenticatorTypes() {
+ public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getAuthenticatorTypes: "
+ + "for user id " + userId
+ "caller's uid " + Binder.getCallingUid()
+ ", pid " + Binder.getCallingPid());
}
- final int userId = UserHandle.getCallingUserId();
+ // Only allow the system process to read accounts of other users
+ enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
+ + " trying get authenticator types for " + userId);
final long identityToken = clearCallingIdentity();
try {
Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
@@ -562,6 +565,16 @@
}
}
+ private void enforceCrossUserPermission(int userId, String errorMessage) {
+ if (userId != UserHandle.getCallingUserId()
+ && Binder.getCallingUid() != Process.myUid()
+ && mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(errorMessage);
+ }
+ }
+
@Override
public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -1567,14 +1580,8 @@
final Account account, final Bundle options, final boolean expectActivityLaunch,
int userId) {
// Only allow the system process to read accounts of other users
- if (userId != UserHandle.getCallingUserId()
- && Binder.getCallingUid() != Process.myUid()
- && mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("User " + UserHandle.getCallingUserId()
+ enforceCrossUserPermission(userId, "User " + UserHandle.getCallingUserId()
+ " trying to confirm account credentials for " + userId);
- }
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "confirmCredentials: " + account
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 61ba6e0..697e1f2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -270,7 +270,7 @@
static final boolean IS_USER_BUILD = "user".equals(Build.TYPE);
// Maximum number of recent tasks that we can remember.
- static final int MAX_RECENT_TASKS = ActivityManager.isLowRamDeviceStatic() ? 10 : 20;
+ static final int MAX_RECENT_TASKS = ActivityManager.isLowRamDeviceStatic() ? 10 : 200;
// Amount of time after a call to stopAppSwitches() during which we will
// prevent further untrusted switches from happening.
@@ -2883,16 +2883,17 @@
}
/*
- * Add shared application GID so applications can share some
- * resources like shared libraries
+ * Add shared application and profile GIDs so applications can share some
+ * resources like shared libraries and access user-wide resources
*/
if (permGids == null) {
- gids = new int[1];
+ gids = new int[2];
} else {
- gids = new int[permGids.length + 1];
- System.arraycopy(permGids, 0, gids, 1, permGids.length);
+ gids = new int[permGids.length + 2];
+ System.arraycopy(permGids, 0, gids, 2, permGids.length);
}
gids[0] = UserHandle.getSharedAppGid(UserHandle.getAppId(uid));
+ gids[1] = UserHandle.getUserGid(UserHandle.getUserId(uid));
}
if (mFactoryTest != FactoryTest.FACTORY_TEST_OFF) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
@@ -2978,6 +2979,10 @@
}
}
buf.append("}");
+ if (requiredAbi != null) {
+ buf.append(" abi=");
+ buf.append(requiredAbi);
+ }
Slog.i(TAG, buf.toString());
app.setPid(startResult.pid);
app.usingWrapper = startResult.usingWrapper;
@@ -3533,6 +3538,9 @@
// Remove any existing entries that are the same kind of task.
final Intent intent = task.intent;
final boolean document = intent != null && intent.isDocument();
+ final ComponentName comp = intent.getComponent();
+
+ int maxRecents = task.maxRecents - 1;
for (int i=0; i<N; i++) {
TaskRecord tr = mRecentTasks.get(i);
if (task != tr) {
@@ -3544,14 +3552,24 @@
(intent == null || !intent.filterEquals(trIntent))) {
continue;
}
- if (document || trIntent != null && trIntent.isDocument()) {
- // Document tasks do not match other tasks.
+ final boolean trIsDocument = trIntent != null && trIntent.isDocument();
+ if (document && trIsDocument) {
+ // These are the same document activity (not necessarily the same doc).
+ if (maxRecents > 0) {
+ --maxRecents;
+ continue;
+ }
+ // Hit the maximum number of documents for this task. Fall through
+ // and remove this document from recents.
+ } else if (document || trIsDocument) {
+ // Only one of these is a document. Not the droid we're looking for.
continue;
}
}
// Either task and tr are the same or, their affinities match or their intents match
- // and neither of them is a document.
+ // and neither of them is a document, or they are documents using the same activity
+ // and their maxRecents has been reached.
tr.disposeThumbnail();
mRecentTasks.remove(i);
i--;
@@ -3561,6 +3579,7 @@
// specified, then replace it with the existing recent task.
task = tr;
}
+ mTaskPersister.notify(tr, false);
}
if (N >= MAX_RECENT_TASKS) {
mRecentTasks.remove(N-1).disposeThumbnail();
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 32722bc..11f855e 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -906,6 +906,9 @@
}
startTime = 0;
finishLaunchTickingLocked();
+ if (task != null) {
+ task.hasBeenVisible = true;
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ba12374..03ce530 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -32,12 +32,12 @@
import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
+import static com.android.server.am.ActivityStackSupervisor.DEBUG_CONTAINERS;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
-import android.service.voice.IVoiceInteractionSession;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.Watchdog;
@@ -74,6 +74,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
import android.util.EventLog;
import android.util.Slog;
import android.view.Display;
@@ -549,14 +550,16 @@
if (DEBUG_TASKS) Slog.d(TAG, "Found matching affinity!");
return r;
}
- } else if (taskIntent != null && taskIntent.getComponent().equals(cls) &&
+ } else if (taskIntent != null && taskIntent.getComponent() != null &&
+ taskIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
//dump();
if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
+ r.intent);
return r;
- } else if (affinityIntent != null && affinityIntent.getComponent().equals(cls) &&
+ } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
+ affinityIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
//dump();
@@ -1403,7 +1406,7 @@
ActivityRecord parent = mActivityContainer.mParentActivity;
if ((parent != null && parent.state != ActivityState.RESUMED) ||
- !mActivityContainer.isAttached()) {
+ !mActivityContainer.isAttachedLocked()) {
// Do not resume this stack if its parent is not resumed.
// TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st.
return false;
@@ -2675,11 +2678,14 @@
|| prevState == ActivityState.INITIALIZING) {
// If this activity is already stopped, we can just finish
// it right now.
- boolean activityRemoved = destroyActivityLocked(r, true,
- oomAdj, "finish-imm");
+ r.makeFinishing();
+ boolean activityRemoved = destroyActivityLocked(r, true, oomAdj, "finish-imm");
if (activityRemoved) {
mStackSupervisor.resumeTopActivitiesLocked();
}
+ if (DEBUG_CONTAINERS) Slog.d(TAG,
+ "destroyActivityLocked: finishCurrentActivityLocked r=" + r +
+ " destroy returned removed=" + activityRemoved);
return activityRemoved ? null : r;
}
@@ -3042,6 +3048,7 @@
if (r != null) {
mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);
}
+ if (DEBUG_CONTAINERS) Slog.d(TAG, "activityDestroyedLocked: r=" + r);
if (isInStackLocked(token) != null) {
if (r.state == ActivityState.DESTROYING) {
@@ -3801,6 +3808,7 @@
mStacks.remove(this);
mStacks.add(0, this);
}
+ mActivityContainer.onTaskListEmptyLocked();
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index dc4ad48..d58b97c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -111,6 +111,7 @@
static final boolean DEBUG_STATES = DEBUG || false;
static final boolean DEBUG_IDLE = DEBUG || false;
static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
+ static final boolean DEBUG_CONTAINERS = DEBUG || false;
public static final int HOME_STACK_ID = 0;
@@ -134,6 +135,8 @@
static final int CONTAINER_CALLBACK_VISIBILITY = FIRST_SUPERVISOR_STACK_MSG + 8;
static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9;
static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10;
+ static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
+ static final int CONTAINER_TASK_LIST_EMPTY_TIMEOUT = FIRST_SUPERVISOR_STACK_MSG + 12;
private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
@@ -238,7 +241,7 @@
// TODO: Add listener for removal of references.
/** Mapping from (ActivityStack/TaskStack).mStackId to their current state */
- SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
+ private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>();
/** Mapping from displayId to display current state */
private final SparseArray<ActivityDisplay> mActivityDisplays =
@@ -1268,7 +1271,7 @@
// to ensure that it is safe to do so. If the upcoming activity will also
// be part of the voice session, we can only launch it if it has explicitly
// said it supports the VOICE category, or it is a part of the calling app.
- if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
try {
if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
@@ -1485,6 +1488,47 @@
int launchFlags = intent.getFlags();
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
+ (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+ r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK)) {
+ // We have a conflict between the Intent and the Activity manifest, manifest wins.
+ Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
+ "\"singleInstance\" or \"singleTask\"");
+ launchFlags &=
+ ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ } else {
+ switch (r.info.documentLaunchMode) {
+ case ActivityInfo.DOCUMENT_LAUNCH_NONE:
+ break;
+ case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+ break;
+ case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+ break;
+ case ActivityInfo.DOCUMENT_LAUNCH_NEVER:
+ launchFlags &= ~Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+ break;
+ }
+ }
+
+ if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ // For whatever reason this activity is being launched into a new
+ // task... yet the caller has requested a result back. Well, that
+ // is pretty messed up, so instead immediately send back a cancel
+ // and let the new task continue launched as normal without a
+ // dependency on its originator.
+ Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
+ r.resultTo.task.stack.sendActivityResultLocked(-1,
+ r.resultTo, r.resultWho, r.requestCode,
+ Activity.RESULT_CANCELED, null);
+ r.resultTo = null;
+ }
+
+ if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 && r.resultTo == null) {
+ launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
+ }
+
// We'll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
@@ -1515,20 +1559,6 @@
}
}
- 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);
- launchFlags = intent.getFlags();
- break;
- case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
- launchFlags = intent.getFlags();
- break;
- }
- final boolean newDocument = intent.isDocument();
if (sourceRecord == null) {
// This activity is not being started from another... in this
// case we -always- start a new task.
@@ -1537,11 +1567,6 @@
"Intent.FLAG_ACTIVITY_NEW_TASK for: " + intent);
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
}
- } else if (newDocument) {
- if (r.launchMode != ActivityInfo.LAUNCH_MULTIPLE) {
- Slog.w(TAG, "FLAG_ACTIVITY_NEW_DOCUMENT and launchMode != \"standard\"");
- r.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- }
} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// The original activity who is starting us is running as a single
// instance... this new activity it is starting must go on its
@@ -1580,18 +1605,7 @@
sourceStack = null;
}
- if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
- // For whatever reason this activity is being launched into a new
- // task... yet the caller has requested a result back. Well, that
- // is pretty messed up, so instead immediately send back a cancel
- // and let the new task continue launched as normal without a
- // dependency on its originator.
- Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
- r.resultTo.task.stack.sendActivityResultLocked(-1,
- r.resultTo, r.resultWho, r.requestCode,
- Activity.RESULT_CANCELED, null);
- r.resultTo = null;
- }
+ intent.setFlags(launchFlags);
boolean addingToTask = false;
boolean movedHome = false;
@@ -1826,7 +1840,7 @@
// Should this be considered a new task?
if (r.resultTo == null && !addingToTask
- && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
+ && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
if (isLockTaskModeViolation(reuseTask)) {
Slog.e(TAG, "Attempted Lock Task Mode violation r=" + r);
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
@@ -2243,8 +2257,10 @@
ActivityContainer createActivityContainer(ActivityRecord parentActivity,
IActivityContainerCallback callback) {
- ActivityContainer activityContainer = new VirtualActivityContainer(parentActivity, callback);
+ ActivityContainer activityContainer =
+ new VirtualActivityContainer(parentActivity, callback);
mActivityContainers.put(activityContainer.mStackId, activityContainer);
+ if (DEBUG_CONTAINERS) Slog.d(TAG, "createActivityContainer: " + activityContainer);
parentActivity.mChildContainers.add(activityContainer);
return activityContainer;
}
@@ -2253,6 +2269,8 @@
final ArrayList<ActivityContainer> childStacks = parentActivity.mChildContainers;
for (int containerNdx = childStacks.size() - 1; containerNdx >= 0; --containerNdx) {
ActivityContainer container = childStacks.remove(containerNdx);
+ if (DEBUG_CONTAINERS) Slog.d(TAG, "removeChildActivityContainers: removing " +
+ container);
container.release();
}
}
@@ -2260,11 +2278,8 @@
void deleteActivityContainer(IActivityContainer container) {
ActivityContainer activityContainer = (ActivityContainer)container;
if (activityContainer != null) {
- activityContainer.mStack.finishAllActivitiesLocked();
- final ActivityRecord parent = activityContainer.mParentActivity;
- if (parent != null) {
- parent.mChildContainers.remove(activityContainer);
- }
+ if (DEBUG_CONTAINERS) Slog.d(TAG, "deleteActivityContainer: ",
+ new RuntimeException("here").fillInStackTrace());
final int stackId = activityContainer.mStackId;
mActivityContainers.remove(stackId);
mWindowManager.removeStack(stackId);
@@ -2873,16 +2888,19 @@
@Override
public void onDisplayAdded(int displayId) {
+ Slog.v(TAG, "Display added displayId=" + displayId);
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0));
}
@Override
public void onDisplayRemoved(int displayId) {
+ Slog.v(TAG, "Display removed displayId=" + displayId);
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_REMOVED, displayId, 0));
}
@Override
public void onDisplayChanged(int displayId) {
+ Slog.v(TAG, "Display changed displayId=" + displayId);
mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
}
@@ -3074,12 +3092,14 @@
} break;
case CONTAINER_CALLBACK_VISIBILITY: {
final ActivityContainer container = (ActivityContainer) msg.obj;
- try {
- // We only send this message if mCallback is non-null.
- container.mCallback.setVisible(container.asBinder(), msg.arg1 == 1);
- } catch (RemoteException e) {
+ final IActivityContainerCallback callback = container.mCallback;
+ if (callback != null) {
+ try {
+ callback.setVisible(container.asBinder(), msg.arg1 == 1);
+ } catch (RemoteException e) {
+ }
}
- }
+ } break;
case LOCK_TASK_START_MSG: {
// When lock task starts, we disable the status bars.
try {
@@ -3091,8 +3111,7 @@
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
- break;
- }
+ } break;
case LOCK_TASK_END_MSG: {
// When lock task ends, we enable the status bars.
try {
@@ -3104,15 +3123,31 @@
} catch (RemoteException ex) {
throw new RuntimeException(ex);
}
- break;
- }
+ } break;
+ case CONTAINER_CALLBACK_TASK_LIST_EMPTY: {
+ final ActivityContainer container = (ActivityContainer) msg.obj;
+ final IActivityContainerCallback callback = container.mCallback;
+ if (callback != null) {
+ try {
+ callback.onAllActivitiesComplete(container.asBinder());
+ } catch (RemoteException e) {
+ }
+ }
+ } break;
+ case CONTAINER_TASK_LIST_EMPTY_TIMEOUT: {
+ synchronized (mService) {
+ Slog.w(TAG, "Timeout waiting for all activities in task to finish. " +
+ msg.obj);
+ ((ActivityContainer) msg.obj).onTaskListEmptyLocked();
+ }
+ } break;
}
}
}
class ActivityContainer extends android.app.IActivityContainer.Stub {
final static int FORCE_NEW_TASK_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION;
final int mStackId;
IActivityContainerCallback mCallback = null;
final ActivityStack mStack;
@@ -3162,8 +3197,10 @@
@Override
public int getDisplayId() {
- if (mActivityDisplay != null) {
- return mActivityDisplay.mDisplayId;
+ synchronized (mService) {
+ if (mActivityDisplay != null) {
+ return mActivityDisplay.mDisplayId;
+ }
}
return -1;
}
@@ -3172,10 +3209,12 @@
public boolean injectEvent(InputEvent event) {
final long origId = Binder.clearCallingIdentity();
try {
- if (mActivityDisplay != null) {
- return mInputManagerInternal.injectInputEvent(event,
- mActivityDisplay.mDisplayId,
- InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ synchronized (mService) {
+ if (mActivityDisplay != null) {
+ return mInputManagerInternal.injectInputEvent(event,
+ mActivityDisplay.mDisplayId,
+ InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ }
}
return false;
} finally {
@@ -3185,10 +3224,23 @@
@Override
public void release() {
- mContainerState = CONTAINER_STATE_FINISHING;
- mStack.finishAllActivitiesLocked();
- detachLocked();
- mWindowManager.removeStack(mStackId);
+ synchronized (mService) {
+ if (mContainerState == CONTAINER_STATE_FINISHING) {
+ return;
+ }
+ mContainerState = CONTAINER_STATE_FINISHING;
+
+ final Message msg =
+ mHandler.obtainMessage(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
+ mHandler.sendMessageDelayed(msg, 1000);
+
+ long origId = Binder.clearCallingIdentity();
+ try {
+ mStack.finishAllActivitiesLocked();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
}
private void detachLocked() {
@@ -3279,15 +3331,17 @@
return ActivityStackSupervisor.this;
}
- boolean isAttached() {
+ boolean isAttachedLocked() {
return mActivityDisplay != null;
}
void getBounds(Point outBounds) {
- if (mActivityDisplay != null) {
- mActivityDisplay.getBounds(outBounds);
- } else {
- outBounds.set(0, 0);
+ synchronized (mService) {
+ if (mActivityDisplay != null) {
+ mActivityDisplay.getBounds(outBounds);
+ } else {
+ outBounds.set(0, 0);
+ }
}
}
@@ -3310,6 +3364,15 @@
return true;
}
+ void onTaskListEmptyLocked() {
+ mHandler.removeMessages(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this);
+ if (!mStack.isHomeStack()) {
+ detachLocked();
+ deleteActivityContainer(this);
+ }
+ mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget();
+ }
+
@Override
public String toString() {
return mIdString + (mActivityDisplay == null ? "N" : "A");
@@ -3325,7 +3388,7 @@
mParentActivity = parent;
mCallback = callback;
mContainerState = CONTAINER_STATE_NO_SURFACE;
- mIdString = "VirtualActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}";
+ mIdString = "VirtualActivityContainer{" + mStackId + ", parent=" + mParentActivity + "}";
}
@Override
@@ -3371,22 +3434,22 @@
}
}
- setSurfaceIfReady();
+ setSurfaceIfReadyLocked();
if (DEBUG_STACK) Slog.d(TAG, "setSurface: " + this + " to display="
+ virtualActivityDisplay);
}
@Override
- boolean isAttached() {
- return mSurface != null && super.isAttached();
+ boolean isAttachedLocked() {
+ return mSurface != null && super.isAttachedLocked();
}
@Override
void setDrawn() {
synchronized (mService) {
mDrawn = true;
- setSurfaceIfReady();
+ setSurfaceIfReadyLocked();
}
}
@@ -3396,8 +3459,8 @@
return false;
}
- private void setSurfaceIfReady() {
- if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn +
+ private void setSurfaceIfReadyLocked() {
+ if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReadyLocked: mDrawn=" + mDrawn +
" mContainerState=" + mContainerState + " mSurface=" + mSurface);
if (mDrawn && mSurface != null && mContainerState == CONTAINER_STATE_NO_SURFACE) {
((VirtualActivityDisplay) mActivityDisplay).setSurface(mSurface);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 249422b..eb253eb 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -623,10 +623,14 @@
pw.println(" --charged: only output data since last charged.");
pw.println(" --reset: reset the stats, clearing all current data.");
pw.println(" --write: force write current collected stats to disk.");
- pw.println(" --enable: enable an option: full-wake-history, no-auto-reset.");
- pw.println(" --disable: disable an option: full-wake-history, no-auto-reset.");
- pw.println(" -h: print this help text.");
pw.println(" <package.name>: optional name of package to filter output by.");
+ pw.println(" -h: print this help text.");
+ pw.println("Battery stats (batterystats) commands:");
+ pw.println(" enable|disable <option>");
+ pw.println(" Enable or disable a running option. Option state is not saved across boots.");
+ pw.println(" Options are:");
+ pw.println(" full-wake-history: include wake_lock_in battery history, full wake details.");
+ pw.println(" no-auto-reset: don't automatically reset stats when unplugged");
}
private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
@@ -665,6 +669,7 @@
int flags = 0;
boolean isCheckin = false;
boolean noOutput = false;
+ boolean writeData = false;
long historyStart = -1;
int reqUid = -1;
if (args != null) {
@@ -683,6 +688,7 @@
return;
}
historyStart = Long.parseLong(args[i]);
+ writeData = true;
} else if ("-c".equals(arg)) {
isCheckin = true;
flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
@@ -702,14 +708,14 @@
pw.println("Battery stats written.");
noOutput = true;
}
- } else if ("--enable".equals(arg)) {
+ } else if ("--enable".equals(arg) || "enable".equals(arg)) {
i = doEnableOrDisable(pw, i, args, true);
if (i < 0) {
return;
}
pw.println("Enabled: " + args[i]);
return;
- } else if ("--disable".equals(arg)) {
+ } else if ("--disable".equals(arg) || "disable".equals(arg)) {
i = doEnableOrDisable(pw, i, args, false);
if (i < 0) {
return;
@@ -745,10 +751,16 @@
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
synchronized (mStats) {
mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
+ if (writeData) {
+ mStats.writeAsyncLocked();
+ }
}
} else {
synchronized (mStats) {
mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
+ if (writeData) {
+ mStats.writeAsyncLocked();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 3bfaca9..bb289fa 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -227,7 +227,7 @@
for (int fileNdx = 0; fileNdx < files.length; ++fileNdx) {
File file = files[fileNdx];
String filename = file.getName();
- final int taskIdEnd = filename.indexOf('_') + 1;
+ final int taskIdEnd = filename.indexOf('_');
if (taskIdEnd > 0) {
final int taskId;
try {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index ce83ae6..81a0b36 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -73,6 +73,7 @@
boolean rootWasReset; // True if the intent at the root of the task had
// the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag.
boolean askedCompatMode;// Have asked the user about compat mode for this task.
+ boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
String stringName; // caching of toString() result.
int userId; // user for which this task was created
@@ -96,6 +97,7 @@
/** Takes on same value as first root activity */
boolean isPersistable = false;
+ int maxRecents;
/** Only used for persistable tasks, otherwise 0. The last time this task was moved. Used for
* determining the order when restoring. Sign indicates whether last task movement was to front
@@ -104,6 +106,7 @@
/** True if persistable, has changed, and has not yet been persisted */
boolean needsPersisting = false;
+
/** Launch the home activity when leaving this task. Will be false for tasks that are not on
* Display.DEFAULT_DISPLAY. */
boolean mOnTopOfHome = false;
@@ -301,6 +304,8 @@
if (mActivities.isEmpty()) {
taskType = r.mActivityType;
isPersistable = r.isPersistable();
+ // Clamp to [1, 100].
+ maxRecents = Math.min(Math.max(r.info.maxRecents, 1), 100);
} else {
// Otherwise make all added activities match this one.
r.mActivityType = taskType;
@@ -324,8 +329,12 @@
}
boolean autoRemoveFromRecents() {
- return intent != null &&
- (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0;
+ // We will automatically remove the task either if it has explicitly asked for
+ // this, or it is empty and has never contained an activity that got shown to
+ // the user.
+ return (intent != null &&
+ (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0) ||
+ (mActivities.isEmpty() && !hasBeenVisible);
}
/**
@@ -643,8 +652,9 @@
final int numActivities = activities.size();
for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
- if (!r.isPersistable() || (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
- Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) {
+ if (!r.isPersistable() || (activityNdx > 0 &&
+ (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0)) {
+ // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
break;
}
out.startTag(null, TAG_ACTIVITY);
@@ -795,7 +805,8 @@
}
pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail);
pw.print(" lastDescription="); pw.println(lastDescription);
- pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
+ pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible);
+ pw.print(" lastActiveTime="); pw.print(lastActiveTime);
pw.print(" (inactive for ");
pw.print((getInactiveDuration()/1000)); pw.println("s)");
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index c93f85d..ec3389b 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1346,7 +1346,7 @@
if (iface != null) {
String[] dnsServers = mDefaultDnsServers;
- Collection<InetAddress> dnses = linkProperties.getDnses();
+ Collection<InetAddress> dnses = linkProperties.getDnsServers();
if (dnses != null) {
// we currently only handle IPv4
ArrayList<InetAddress> v4Dnses =
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 8fd55e7..3b55bfc 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -365,15 +365,8 @@
Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
SyncStorageEngine.EndPoint info;
- if (!request.hasAuthority()) {
- // Extra permissions checking for sync service.
- verifySignatureForPackage(callerUid,
- request.getService().getPackageName(), "sync");
- info = new SyncStorageEngine.EndPoint(request.getService(), userId);
- } else {
- info = new SyncStorageEngine.EndPoint(
- request.getAccount(), request.getProvider(), userId);
- }
+ info = new SyncStorageEngine.EndPoint(
+ request.getAccount(), request.getProvider(), userId);
if (runAtTime < 60) {
Slog.w(TAG, "Requested poll frequency of " + runAtTime
+ " seconds being rounded up to 60 seconds.");
@@ -385,17 +378,10 @@
} else {
long beforeRuntimeMillis = (flextime) * 1000;
long runtimeMillis = runAtTime * 1000;
- if (request.hasAuthority()) {
syncManager.scheduleSync(
request.getAccount(), userId, callerUid, request.getProvider(), extras,
beforeRuntimeMillis, runtimeMillis,
false /* onlyThoseWithUnknownSyncableState */);
- } else {
- syncManager.scheduleSync(
- request.getService(), userId, callerUid, extras,
- beforeRuntimeMillis,
- runtimeMillis); // Empty function.
- }
}
} finally {
restoreCallingIdentity(identityToken);
@@ -442,22 +428,14 @@
SyncManager syncManager = getSyncManager();
if (syncManager == null) return;
int userId = UserHandle.getCallingUserId();
- int callerUid = Binder.getCallingUid();
long identityToken = clearCallingIdentity();
try {
SyncStorageEngine.EndPoint info;
Bundle extras = new Bundle(request.getBundle());
- if (request.hasAuthority()) {
- Account account = request.getAccount();
- String provider = request.getProvider();
- info = new SyncStorageEngine.EndPoint(account, provider, userId);
- } else {
- // Only allowed to manipulate syncs for a service which you own.
- ComponentName service = request.getService();
- verifySignatureForPackage(callerUid, service.getPackageName(), "cancel");
- info = new SyncStorageEngine.EndPoint(service, userId);
- }
+ Account account = request.getAccount();
+ String provider = request.getProvider();
+ info = new SyncStorageEngine.EndPoint(account, provider, userId);
if (request.isPeriodic()) {
// Remove periodic sync.
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
@@ -599,20 +577,11 @@
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
- int callerUid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
long identityToken = clearCallingIdentity();
try {
- if (cname == null) {
- return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
- new SyncStorageEngine.EndPoint(account, providerName, userId));
- } else if (account == null && providerName == null) {
- verifySignatureForPackage(callerUid, cname.getPackageName(), "getPeriodicSyncs");
- return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
- new SyncStorageEngine.EndPoint(cname, userId));
- } else {
- throw new IllegalArgumentException("Invalid authority specified");
- }
+ return getSyncManager().getSyncStorageEngine().getPeriodicSyncs(
+ new SyncStorageEngine.EndPoint(account, providerName, userId));
} finally {
restoreCallingIdentity(identityToken);
}
@@ -656,45 +625,6 @@
}
}
- public void setServiceActive(ComponentName cname, boolean active) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
- "no permission to write the sync settings");
- verifySignatureForPackage(Binder.getCallingUid(), cname.getPackageName(),
- "setServiceActive");
-
- int userId = UserHandle.getCallingUserId();
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- syncManager.getSyncStorageEngine().setIsTargetServiceActive(
- cname, userId, active);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- }
-
- public boolean isServiceActive(ComponentName cname) {
- mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
- "no permission to read the sync settings");
- verifySignatureForPackage(Binder.getCallingUid(), cname.getPackageName(),
- "isServiceActive");
-
- int userId = UserHandle.getCallingUserId();
- long identityToken = clearCallingIdentity();
- try {
- SyncManager syncManager = getSyncManager();
- if (syncManager != null) {
- return syncManager.getSyncStorageEngine()
- .getIsTargetServiceActive(cname, userId);
- }
- } finally {
- restoreCallingIdentity(identityToken);
- }
- return false;
- }
-
@Override
public boolean getMasterSyncAutomatically() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
@@ -741,18 +671,11 @@
if (syncManager == null) {
return false;
}
- if (cname == null) {
- return syncManager.getSyncStorageEngine().isSyncActive(
- new SyncStorageEngine.EndPoint(account, authority, userId));
- } else if (account == null && authority == null) {
- verifySignatureForPackage(callingUid, cname.getPackageName(), "isSyncActive");
- return syncManager.getSyncStorageEngine().isSyncActive(
- new SyncStorageEngine.EndPoint(cname, userId));
- }
+ return syncManager.getSyncStorageEngine().isSyncActive(
+ new SyncStorageEngine.EndPoint(account, authority, userId));
} finally {
restoreCallingIdentity(identityToken);
}
- return false;
}
public List<SyncInfo> getCurrentSyncs() {
@@ -784,11 +707,8 @@
return null;
}
SyncStorageEngine.EndPoint info;
- if (cname == null) {
+ if (!(account == null || authority == null)) {
info = new SyncStorageEngine.EndPoint(account, authority, userId);
- } else if (account == null && authority == null) {
- verifySignatureForPackage(callerUid, cname.getPackageName(), "getSyncStatus");
- info = new SyncStorageEngine.EndPoint(cname, userId);
} else {
throw new IllegalArgumentException("Must call sync status with valid authority");
}
@@ -810,11 +730,8 @@
try {
SyncStorageEngine.EndPoint info;
- if (cname == null) {
+ if (!(account == null || authority == null)) {
info = new SyncStorageEngine.EndPoint(account, authority, userId);
- } else if (account == null && authority == null) {
- verifySignatureForPackage(callerUid, cname.getPackageName(), "isSyncPending");
- info = new SyncStorageEngine.EndPoint(cname, userId);
} else {
throw new IllegalArgumentException("Invalid authority specified");
}
@@ -855,30 +772,6 @@
}
/**
- * Helper to verify that the provided package name shares the same cert as the caller.
- * @param callerUid uid of the calling process.
- * @param packageName package to verify against package of calling application.
- * @param tag a tag to use when throwing an exception if the signatures don't
- * match. Cannot be null.
- * @return true if the calling application and the provided package are signed with the same
- * certificate.
- */
- private boolean verifySignatureForPackage(int callerUid, String packageName, String tag) {
- PackageManager pm = mContext.getPackageManager();
- try {
- int serviceUid = pm.getApplicationInfo(packageName, 0).uid;
- if (pm.checkSignatures(callerUid, serviceUid) == PackageManager.SIGNATURE_MATCH) {
- return true;
- } else {
- throw new SecurityException(tag + ": Caller certificate does not match that for - "
- + packageName);
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException(tag + ": " + packageName + " package not found.");
- }
- }
-
- /**
* Hide this class since it is not part of api,
* but current unittest framework requires it to be public
* @hide
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 35c494d..9499370 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -925,10 +925,7 @@
period,
flextime);
} else {
- toUpdate = new PeriodicSync(info.service,
- extras,
- period,
- flextime);
+ return;
}
AuthorityInfo authority =
getOrCreateAuthorityLocked(info, -1, false);
@@ -1246,7 +1243,6 @@
authorityInfo.ident,
authorityInfo.target.account,
authorityInfo.target.provider,
- authorityInfo.target.service,
activeSyncContext.mStartTime);
getCurrentSyncs(authorityInfo.target.userId).add(syncInfo);
}
@@ -1262,8 +1258,7 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "removeActiveSync: account=" + syncInfo.account
+ " user=" + userId
- + " auth=" + syncInfo.authority
- + " service=" + syncInfo.service);
+ + " auth=" + syncInfo.authority);
}
getCurrentSyncs(userId).remove(syncInfo);
}
@@ -2109,12 +2104,8 @@
extras,
period, flextime);
} else {
- periodicSync =
- new PeriodicSync(
- authorityInfo.target.service,
- extras,
- period,
- flextime);
+ Log.e(TAG, "Unknown target.");
+ return null;
}
authorityInfo.periodicSyncs.add(periodicSync);
return periodicSync;
@@ -2700,7 +2691,10 @@
if (authorityInfo.target.target_provider) {
req.setSyncAdapter(authorityInfo.target.account, authorityInfo.target.provider);
} else {
- req.setSyncAdapter(authorityInfo.target.service);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Unknown target, skipping sync request.");
+ }
+ return;
}
ContentResolver.requestSync(req.build());
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index a98c340..654b574 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -555,8 +555,14 @@
// Turn the screen on. The contents of the screen may not yet
// be visible if the electron beam has not been dismissed because
// its last frame of animation is solid black.
- setScreenState(mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE
- ? Display.STATE_DOZING : Display.STATE_ON);
+
+ if (mPowerRequest.screenState == DisplayPowerRequest.SCREEN_STATE_DOZE) {
+ if (!mScreenBrightnessRampAnimator.isAnimating()) {
+ setScreenState(Display.STATE_DOZING);
+ }
+ } else {
+ setScreenState(Display.STATE_ON);
+ }
if (mPowerRequest.blockScreenOn
&& mPowerState.getElectronBeamLevel() == 0.0f) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7f43e43..e80aecd 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -111,12 +111,15 @@
}
}
- static boolean shouldBlank(int state) {
- return state == Display.STATE_OFF;
- }
-
- static boolean shouldUnblank(int state) {
- return state == Display.STATE_ON || state == Display.STATE_DOZING;
+ static int getPowerModeForState(int state) {
+ switch (state) {
+ case Display.STATE_OFF:
+ return SurfaceControl.POWER_MODE_OFF;
+ case Display.STATE_DOZING:
+ return SurfaceControl.POWER_MODE_DOZE;
+ default:
+ return SurfaceControl.POWER_MODE_NORMAL;
+ }
}
private final class LocalDisplayDevice extends DisplayDevice {
@@ -204,11 +207,8 @@
@Override
public void requestDisplayStateLocked(int state) {
if (mState != state) {
- if (shouldBlank(state) && !shouldBlank(mState)) {
- SurfaceControl.blankDisplay(getDisplayTokenLocked());
- } else if (shouldUnblank(state) && !shouldUnblank(mState)) {
- SurfaceControl.unblankDisplay(getDisplayTokenLocked());
- }
+ SurfaceControl.setDisplayPowerMode(getDisplayTokenLocked(),
+ getPowerModeForState(state));
mState = state;
updateDeviceInfoLocked();
}
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
new file mode 100644
index 0000000..32bcb69
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Handles CEC command <Active Source>.
+ *
+ * <p>Used by feature actions that need to handle the command in their flow.
+ */
+final class ActiveSourceHandler {
+ private static final String TAG = "ActiveSourceHandler";
+
+ private final HdmiControlService mService;
+ private final int mSourceAddress;
+ private final int mSourcePath;
+ @Nullable private final IHdmiControlCallback mCallback;
+
+ static ActiveSourceHandler create(HdmiControlService service, int sourceAddress,
+ int sourcePath, IHdmiControlCallback callback) {
+ if (service == null) {
+ Slog.e(TAG, "Wrong arguments");
+ return null;
+ }
+ return new ActiveSourceHandler(service, sourceAddress, sourcePath, callback);
+ }
+
+ private ActiveSourceHandler(HdmiControlService service, int sourceAddress, int sourcePath,
+ IHdmiControlCallback callback) {
+ mService = service;
+ mSourceAddress = sourceAddress;
+ mSourcePath = sourcePath;
+ mCallback = callback;
+ }
+
+ /**
+ * Handles the incoming active source command.
+ *
+ * @param deviceLogicalAddress logical address of the device to be the active source
+ * @param routingPath routing path of the device to be the active source
+ */
+ void process(int deviceLogicalAddress, int routingPath) {
+ if (mSourcePath == routingPath && mService.getActiveSource() == mSourceAddress) {
+ invokeCallback(HdmiCec.RESULT_SUCCESS);
+ return;
+ }
+ HdmiCecDeviceInfo device = mService.getDeviceInfo(deviceLogicalAddress);
+ if (device == null) {
+ // TODO: Start new device action (Device Discovery) sequence 5.
+ }
+
+ if (!mService.isInPresetInstallationMode()) {
+ int prevActiveInput = mService.getActiveInput();
+ mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+ if (prevActiveInput != mService.getActiveInput()) {
+ // TODO: change port input here.
+ }
+ invokeCallback(HdmiCec.RESULT_SUCCESS);
+ } else {
+ // TV is in a mode that should keep its current source/input from
+ // being changed for its operation. Reclaim the active source
+ // or switch the port back to the one used for the current mode.
+ if (mService.getActiveSource() == mSourceAddress) {
+ HdmiCecMessage activeSource =
+ HdmiCecMessageBuilder.buildActiveSource(mSourceAddress, mSourcePath);
+ mService.sendCecCommand(activeSource);
+ mService.updateActiveDevice(deviceLogicalAddress, routingPath);
+ invokeCallback(HdmiCec.RESULT_SUCCESS);
+ } else {
+ int activePath = mService.getActivePath();
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mSourceAddress,
+ routingPath, activePath));
+ // TODO: Start port select action here
+ // PortSelectAction action = new PortSelectAction(mService, mSourceAddress,
+ // activePath, mCallback);
+ // mService.addActionAndStart(action);
+ }
+ }
+ }
+
+ private void invokeCallback(int result) {
+ if (mCallback == null) {
+ return;
+ }
+ try {
+ mCallback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Callback failed:" + e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index 579f89f..d36fc2c 100644
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -95,10 +95,10 @@
private int mProcessedDeviceCount = 0;
/**
- * @Constructor
+ * Constructor.
*
- * @param service
- * @param sourceAddress
+ * @param service an instance of {@link HdmiControlService}.
+ * @param sourceAddress a logical address which initiates this action
*/
DeviceDiscoveryAction(HdmiControlService service, int sourceAddress,
DeviceDiscoveryCallback callback) {
@@ -124,7 +124,7 @@
allocateDevices(ackedAddress);
startPhysicalAddressStage();
}
- }, DEVICE_POLLING_RETRY);
+ }, HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, DEVICE_POLLING_RETRY);
return true;
}
@@ -154,6 +154,11 @@
}
mActionTimer.clearTimerMessage();
+
+ // Check cache first and send request if not exist.
+ if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS)) {
+ return;
+ }
sendCommand(HdmiCecMessageBuilder.buildGivePhysicalAddress(mSourceAddress, address));
addTimer(mState, TIMEOUT_MS);
}
@@ -173,6 +178,10 @@
}
mActionTimer.clearTimerMessage();
+
+ if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_SET_OSD_NAME)) {
+ return;
+ }
sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress, address));
addTimer(mState, TIMEOUT_MS);
}
@@ -193,10 +202,23 @@
}
mActionTimer.clearTimerMessage();
+
+ if (mayProcessMessageIfCached(address, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
+ return;
+ }
sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress, address));
addTimer(mState, TIMEOUT_MS);
}
+ private boolean mayProcessMessageIfCached(int address, int opcode) {
+ HdmiCecMessage message = mService.getCecMessageCache().getMessage(address, opcode);
+ if (message != null) {
+ processCommand(message);
+ return true;
+ }
+ return false;
+ }
+
@Override
boolean processCommand(HdmiCecMessage cmd) {
switch (mState) {
@@ -237,7 +259,7 @@
byte params[] = cmd.getParams();
if (params.length == 3) {
- current.mPhysicalAddress = ((params[0] & 0xFF) << 8) | (params[1] & 0xFF);
+ current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params);
current.mDeviceType = params[2] & 0xFF;
increaseProcessedDeviceCount();
@@ -285,9 +307,7 @@
byte[] params = cmd.getParams();
if (params.length == 3) {
- int vendorId = ((params[0] & 0xFF) << 16)
- | ((params[1] & 0xFF) << 8)
- | (params[2] & 0xFF);
+ int vendorId = HdmiUtils.threeBytesToInt(params);
current.mVendorId = vendorId;
} else {
Slog.w(TAG, "Invalid vendor id: " + cmd.toString());
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
new file mode 100644
index 0000000..f170de0
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Handles an action that selects a logical device as a new active source.
+ *
+ * Triggered by {@link HdmiTvClient}, attempts to select the given target device
+ * for a new active source. It does its best to wake up the target in standby mode
+ * before issuing the command >Set Stream path<.
+ */
+final class DeviceSelectAction extends FeatureAction {
+ private static final String TAG = "DeviceSelect";
+
+ // Time in milliseconds we wait for the device power status to switch to 'Standby'
+ private static final int TIMEOUT_TRANSIT_TO_STANDBY_MS = 5 * 1000;
+
+ // Time in milliseconds we wait for the device power status to turn to 'On'.
+ private static final int TIMEOUT_POWER_ON_MS = 5 * 1000;
+
+ // Time in milliseconds we wait for <Active Source>.
+ private static final int TIMEOUT_ACTIVE_SOURCE_MS = 20 * 1000;
+
+ // The number of times we try to wake up the target device before we give up
+ // and just send <Set Stream Path>.
+ private static final int LOOP_COUNTER_MAX = 20;
+
+ // State in which we wait for <Report Power Status> to come in response to the command
+ // <Give Device Power Status> we have sent.
+ private static final int STATE_WAIT_FOR_REPORT_POWER_STATUS = 1;
+
+ // State in which we wait for the device power status to switch to 'Standby'.
+ // We wait till the status becomes 'Standby' before we send <Set Stream Path>
+ // to wake up the device again.
+ private static final int STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY = 2;
+
+ // State in which we wait for the device power status to switch to 'on'. We wait
+ // maximum 100 seconds (20 * 5) before we give up and just send <Set Stream Path>.
+ private static final int STATE_WAIT_FOR_DEVICE_POWER_ON = 3;
+
+ // State in which we wait for the <Active Source> in response to the command
+ // <Set Stream Path> we have sent. We wait as much as TIMEOUT_ACTIVE_SOURCE_MS
+ // before we give up and mark the action as failure.
+ private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 4;
+
+ private final HdmiCecDeviceInfo mTarget;
+ private final IHdmiControlCallback mCallback;
+ private final int mSourcePath;
+
+ private int mPowerStatusCounter = 0;
+
+ /**
+ * Constructor.
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param sourceAddress logical address of TV initiating this action
+ * @param sourcePath physical address of TV
+ * @param target target logical device that will be a new active source
+ * @param callback callback object
+ */
+ public DeviceSelectAction(HdmiControlService service, int sourceAddress, int sourcePath,
+ HdmiCecDeviceInfo target, IHdmiControlCallback callback) {
+ super(service, sourceAddress);
+ mCallback = callback;
+ mSourcePath = sourcePath;
+ mTarget = target;
+ }
+
+ @Override
+ public boolean start() {
+ // TODO: Call the logic that display a banner saying the select action got started.
+ queryDevicePowerStatus();
+ return true;
+ }
+
+ private void queryDevicePowerStatus() {
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(
+ mSourceAddress, mTarget.getLogicalAddress()));
+ mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
+ addTimer(mState, TIMEOUT_MS);
+ }
+
+ @Override
+ public boolean processCommand(HdmiCecMessage cmd) {
+ if (cmd.getSource() != mTarget.getLogicalAddress()) {
+ return false;
+ }
+ int opcode = cmd.getOpcode();
+ byte[] params = cmd.getParams();
+
+ switch (mState) {
+ case STATE_WAIT_FOR_REPORT_POWER_STATUS:
+ if (opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS && params.length == 1) {
+ return handleReportPowerStatus(params[0]);
+ }
+ return false;
+ case STATE_WAIT_FOR_ACTIVE_SOURCE:
+ if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE && params.length == 2) {
+ int activePath = HdmiUtils.twoBytesToInt(params);
+ ActiveSourceHandler.create(mService, mSourceAddress, mSourcePath, mCallback)
+ .process(cmd.getSource(), activePath);
+ finish();
+ return true;
+ }
+ return false;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ private boolean handleReportPowerStatus(int powerStatus) {
+ // TODO: Check TV's own status which might have been updated during the action.
+ // If in 'Standby' or 'Transit to standby', remove the banner
+ // and stop this action. Otherwise, send <Set Stream Path>
+ switch (powerStatus) {
+ case HdmiCec.POWER_STATUS_ON:
+ sendSetStreamPath();
+ return true;
+ case HdmiCec.POWER_STATUS_TRANSIENT_TO_STANDBY:
+ if (mPowerStatusCounter < 4) {
+ mState = STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY;
+ addTimer(mState, TIMEOUT_TRANSIT_TO_STANDBY_MS);
+ } else {
+ sendSetStreamPath();
+ }
+ return true;
+ case HdmiCec.POWER_STATUS_STANDBY:
+ if (mPowerStatusCounter == 0) {
+ turnOnDevice();
+ } else {
+ sendSetStreamPath();
+ }
+ return true;
+ case HdmiCec.POWER_STATUS_TRANSIENT_TO_ON:
+ if (mPowerStatusCounter < LOOP_COUNTER_MAX) {
+ mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
+ addTimer(mState, TIMEOUT_POWER_ON_MS);
+ } else {
+ sendSetStreamPath();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void turnOnDevice() {
+ sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER);
+ sendRemoteKeyCommand(HdmiConstants.UI_COMMAND_POWER_ON_FUNCTION);
+ mState = STATE_WAIT_FOR_DEVICE_POWER_ON;
+ addTimer(mState, TIMEOUT_POWER_ON_MS);
+ }
+
+ private void sendSetStreamPath() {
+ sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(
+ mSourceAddress, mTarget.getPhysicalAddress()));
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
+ addTimer(mState, TIMEOUT_ACTIVE_SOURCE_MS);
+ }
+
+ private void sendRemoteKeyCommand(int keyCode) {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress,
+ mTarget.getLogicalAddress(), keyCode));
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress,
+ mTarget.getLogicalAddress()));
+ }
+
+ @Override
+ public void handleTimerEvent(int timeoutState) {
+ if (mState != timeoutState) {
+ Slog.w(TAG, "Timer in a wrong state. Ignored.");
+ return;
+ }
+ switch (mState) {
+ case STATE_WAIT_FOR_REPORT_POWER_STATUS:
+ sendSetStreamPath();
+ break;
+ case STATE_WAIT_FOR_DEVICE_TO_TRANSIT_TO_STANDBY:
+ case STATE_WAIT_FOR_DEVICE_POWER_ON:
+ mPowerStatusCounter++;
+ queryDevicePowerStatus();
+ break;
+ case STATE_WAIT_FOR_ACTIVE_SOURCE:
+ // TODO: Remove the banner
+ // Display banner "Communication failed. Please check your cable or connection"
+ invokeCallback(HdmiCec.RESULT_TIMEOUT);
+ finish();
+ break;
+ }
+ }
+
+ private void invokeCallback(int result) {
+ if (mCallback == null) {
+ return;
+ }
+ try {
+ mCallback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Callback failed:" + e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/FeatureAction.java b/services/core/java/com/android/server/hdmi/FeatureAction.java
index 0ba7773..ae272b4 100644
--- a/services/core/java/com/android/server/hdmi/FeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/FeatureAction.java
@@ -46,8 +46,9 @@
// Timer handler message used for timeout event
protected static final int MSG_TIMEOUT = 100;
- // Default timeout for the incoming command to arrive in response to a request
- protected static final int TIMEOUT_MS = 1000;
+ // Default timeout for the incoming command to arrive in response to a request.
+ // TODO: Consider reading this value from configuration to allow customization.
+ protected static final int TIMEOUT_MS = 2000;
// Default state used in common by all the feature actions.
protected static final int STATE_NONE = 0;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 5141d16..a0c635d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -19,12 +19,14 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiPortInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.util.Predicate;
import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
import libcore.util.EmptyArray;
@@ -47,6 +49,21 @@
final class HdmiCecController {
private static final String TAG = "HdmiCecController";
+ /**
+ * Interface to report allocated logical address.
+ */
+ interface AllocateAddressCallback {
+ /**
+ * Called when a new logical address is allocated.
+ *
+ * @param deviceType requested device type to allocate logical address
+ * @param logicalAddress allocated logical address. If it is
+ * {@link HdmiCec#ADDR_UNREGISTERED}, it means that
+ * it failed to allocate logical address for the given device type
+ */
+ void onAllocated(int deviceType, int logicalAddress);
+ }
+
private static final byte[] EMPTY_BODY = EmptyArray.BYTE;
// A message to pass cec send command to IO looper.
@@ -63,6 +80,22 @@
private static final int RETRY_COUNT_FOR_LOGICAL_ADDRESS_ALLOCATION = 3;
+ // Predicate for whether the given logical address is remote device's one or not.
+ private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
+ @Override
+ public boolean apply(Integer address) {
+ return !isAllocatedLocalDeviceAddress(address);
+ }
+ };
+
+ // Predicate whether the given logical address is system audio's one or not
+ private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() {
+ @Override
+ public boolean apply(Integer address) {
+ return HdmiCec.getTypeFromAddress(address) == HdmiCec.ADDR_AUDIO_SYSTEM;
+ }
+ };
+
// Handler instance to process synchronous I/O (mainly send) message.
private Handler mIoHandler;
@@ -116,45 +149,13 @@
mNativePtr = nativePtr;
}
- /**
- * Perform initialization for each hosted device.
- *
- * @param deviceTypes array of device types
- */
- void initializeLocalDevices(int[] deviceTypes,
- HdmiCecLocalDevice.AddressAllocationCallback callback) {
- assertRunOnServiceThread();
- for (int type : deviceTypes) {
- HdmiCecLocalDevice device = HdmiCecLocalDevice.create(this, type, callback);
- if (device == null) {
- continue;
- }
- // TODO: Consider restoring the local device addresses from persistent storage
- // to allocate the same addresses again if possible.
- device.setPreferredAddress(HdmiCec.ADDR_UNREGISTERED);
- mLocalDevices.put(type, device);
- device.init();
- }
- }
-
- /**
- * Interface to report allocated logical address.
- */
- interface AllocateLogicalAddressCallback {
- /**
- * Called when a new logical address is allocated.
- *
- * @param deviceType requested device type to allocate logical address
- * @param logicalAddress allocated logical address. If it is
- * {@link HdmiCec#ADDR_UNREGISTERED}, it means that
- * it failed to allocate logical address for the given device type
- */
- void onAllocated(int deviceType, int logicalAddress);
+ void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
+ mLocalDevices.put(deviceType, device);
}
/**
* Allocate a new logical address of the given device type. Allocated
- * address will be reported through {@link AllocateLogicalAddressCallback}.
+ * address will be reported through {@link AllocateAddressCallback}.
*
* <p> Declared as package-private, accessed by {@link HdmiControlService} only.
*
@@ -166,7 +167,7 @@
* @param callback callback interface to report allocated logical address to caller
*/
void allocateLogicalAddress(final int deviceType, final int preferredAddress,
- final AllocateLogicalAddressCallback callback) {
+ final AllocateAddressCallback callback) {
assertRunOnServiceThread();
runOnIoThread(new Runnable() {
@@ -178,7 +179,7 @@
}
private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
- final AllocateLogicalAddressCallback callback) {
+ final AllocateAddressCallback callback) {
assertRunOnIoThread();
int startAddress = preferredAddress;
// If preferred address is "unregistered", start address will be the smallest
@@ -275,10 +276,23 @@
* Return a list of all {@link HdmiCecDeviceInfo}.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ *
+ * @param includeLocalDevice whether to add local device or not
*/
- List<HdmiCecDeviceInfo> getDeviceInfoList() {
+ List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
assertRunOnServiceThread();
- return sparseArrayToList(mDeviceInfos);
+ if (includeLocalDevice) {
+ return sparseArrayToList(mDeviceInfos);
+ } else {
+ ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mDeviceInfos.size(); ++i) {
+ HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
+ if (mRemoteDeviceAddressPredicate.apply(info.getLogicalAddress())) {
+ infoList.add(info);
+ }
+ }
+ return infoList;
+ }
}
/**
@@ -295,6 +309,10 @@
return mDeviceInfos.get(logicalAddress);
}
+ HdmiPortInfo[] getPortInfos() {
+ return nativeGetPortInfos(mNativePtr);
+ }
+
/**
* Return the locally hosted logical device of a given type.
*
@@ -374,24 +392,55 @@
}
/**
+ * Pass a option to CEC HAL.
+ *
+ * @param flag a key of option. For more details, look at
+ * {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to
+ * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL}
+ * @param value a value of option. Actual value varies flag. For more
+ * details, look at description of flags
+ */
+ void setOption(int flag, int value) {
+ assertRunOnServiceThread();
+ nativeSetOption(mNativePtr, flag, value);
+ }
+
+ /**
+ * Configure ARC circuit in the hardware logic to start or stop the feature.
+ *
+ * @param enabled whether to enable/disable ARC
+ */
+ void setAudioReturnChannel(boolean enabled) {
+ assertRunOnServiceThread();
+ nativeSetAudioReturnChannel(mNativePtr, enabled);
+ }
+
+ /**
+ * Return the connection status of the specified port
+ *
+ * @param port port number to check connection status
+ * @return true if connected; otherwise, return false
+ */
+ boolean isConnected(int port) {
+ assertRunOnServiceThread();
+ return nativeIsConnected(mNativePtr, port);
+ }
+
+ /**
* Poll all remote devices. It sends <Polling Message> to all remote
* devices.
*
* <p>Declared as package-private. accessed by {@link HdmiControlService} only.
*
* @param callback an interface used to get a list of all remote devices' address
+ * @param pickStrategy strategy how to pick polling candidates
* @param retryCount the number of retry used to send polling message to remote devices
*/
- void pollDevices(DevicePollingCallback callback, int retryCount) {
+ void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
assertRunOnServiceThread();
- // Extract polling candidates. No need to poll against local devices.
- ArrayList<Integer> pollingCandidates = new ArrayList<>();
- for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
- if (!isAllocatedLocalDeviceAddress(i)) {
- pollingCandidates.add(i);
- }
- }
+ // Extract polling candidates. No need to poll against local devices.
+ List<Integer> pollingCandidates = pickPollCandidates(pickStrategy);
runDevicePolling(pollingCandidates, retryCount, callback);
}
@@ -405,6 +454,41 @@
return sparseArrayToList(mLocalDevices);
}
+ private List<Integer> pickPollCandidates(int pickStrategy) {
+ int strategy = pickStrategy & HdmiControlService.POLL_STRATEGY_MASK;
+ Predicate<Integer> pickPredicate = null;
+ switch (strategy) {
+ case HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO:
+ pickPredicate = mSystemAudioAddressPredicate;
+ break;
+ case HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES:
+ default: // The default is POLL_STRATEGY_REMOTES_DEVICES.
+ pickPredicate = mRemoteDeviceAddressPredicate;
+ break;
+ }
+
+ int iterationStrategy = pickStrategy & HdmiControlService.POLL_ITERATION_STRATEGY_MASK;
+ ArrayList<Integer> pollingCandidates = new ArrayList<>();
+ switch (iterationStrategy) {
+ case HdmiControlService.POLL_ITERATION_IN_ORDER:
+ for (int i = HdmiCec.ADDR_TV; i <= HdmiCec.ADDR_SPECIFIC_USE; ++i) {
+ if (pickPredicate.apply(i)) {
+ pollingCandidates.add(i);
+ }
+ }
+ break;
+ case HdmiControlService.POLL_ITERATION_REVERSE_ORDER:
+ default: // The default is reverse order.
+ for (int i = HdmiCec.ADDR_SPECIFIC_USE; i >= HdmiCec.ADDR_TV; --i) {
+ if (pickPredicate.apply(i)) {
+ pollingCandidates.add(i);
+ }
+ }
+ break;
+ }
+ return pollingCandidates;
+ }
+
private static <T> List<T> sparseArrayToList(SparseArray<T> array) {
ArrayList<T> list = new ArrayList<>();
for (int i = 0; i < array.size(); ++i) {
@@ -562,4 +646,8 @@
private static native int nativeGetPhysicalAddress(long controllerPtr);
private static native int nativeGetVersion(long controllerPtr);
private static native int nativeGetVendorId(long controllerPtr);
+ private static native HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
+ private static native void nativeSetOption(long controllerPtr, int flag, int value);
+ private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag);
+ private static native boolean nativeIsConnected(long controllerPtr, int port);
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
deleted file mode 100644
index baae1d9..0000000
--- a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.hdmi;
-
-import android.hardware.hdmi.HdmiCec;
-import android.hardware.hdmi.HdmiCecMessage;
-import android.hardware.hdmi.IHdmiCecListener;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * HdmiCecDevice class represents a CEC logical device characterized
- * by its device type. It is a superclass of those serving concrete device type.
- * Currently we're interested in playback(one of sources), display(sink) device type
- * only. The support for the other types like recorder, audio system will come later.
- *
- * <p>A physical device can contain the functions of
- * more than one logical device, in which case it should create
- * as many logical devices as necessary.
- *
- * <p>Note that if a physical device has multiple instances of a particular
- * functionality, it should advertize only one instance. For instance, if
- * a device has multiple tuners, it should only expose one for control
- * via CEC. In this case, it is up to the device itself to manage multiple tuners.
- *
- * <p>The version of HDMI-CEC protocol supported in this class is 1.3a.
- *
- * <p>Declared as package-private, accessed by HdmiCecService only.
- */
-abstract class HdmiCecDevice {
- private static final String TAG = "HdmiCecDevice";
-
- private final int mType;
-
- // List of listeners to the message/event coming to the device.
- private final List<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>();
- private final Binder mBinder = new Binder();
- private final HdmiCecService mService;
-
- private boolean mIsActiveSource;
-
- /**
- * Factory method that creates HdmiCecDevice instance to the device type.
- */
- public static HdmiCecDevice create(HdmiCecService service, int type) {
- if (type == HdmiCec.DEVICE_PLAYBACK) {
- return new HdmiCecDevicePlayback(service, type);
- } else if (type == HdmiCec.DEVICE_TV) {
- return new HdmiCecDeviceTv(service, type);
- }
- return null;
- }
-
- /**
- * Constructor.
- */
- public HdmiCecDevice(HdmiCecService service, int type) {
- mService = service;
- mType = type;
- mIsActiveSource = false;
- }
-
- /**
- * Called right after the class is instantiated. This method can be used to
- * implement any initialization tasks for the instance.
- */
- abstract public void initialize();
-
- /**
- * Return the binder token that identifies this instance.
- */
- public Binder getToken() {
- return mBinder;
- }
-
- /**
- * Return the service instance.
- */
- public HdmiCecService getService() {
- return mService;
- }
-
- /**
- * Return the type of this device.
- */
- public int getType() {
- return mType;
- }
-
- /**
- * Register a listener to be invoked when events occur.
- *
- * @param listener the listern that will run
- */
- public void addListener(IHdmiCecListener listener) {
- mListeners.add(listener);
- }
-
- /**
- * Remove the listener that was previously registered.
- *
- * @param listener IHdmiCecListener instance to be removed
- */
- public void removeListener(IHdmiCecListener listener) {
- mListeners.remove(listener);
- }
-
- /**
- * Indicate if the device has listeners.
- *
- * @return true if there are listener instances for this device
- */
- public boolean hasListener() {
- return !mListeners.isEmpty();
- }
-
- /**
- * Handle HDMI-CEC message coming to the device by invoking the registered
- * listeners.
- */
- public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
- if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) {
- mIsActiveSource = false;
- }
-
- if (mListeners.size() == 0) {
- return;
- }
- HdmiCecMessage message = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
- for (IHdmiCecListener listener : mListeners) {
- try {
- listener.onMessageReceived(message);
- } catch (RemoteException e) {
- Log.e(TAG, "listener.onMessageReceived failed.");
- }
- }
- }
-
- public void handleHotplug(boolean connected) {
- for (IHdmiCecListener listener : mListeners) {
- try {
- listener.onCableStatusChanged(connected);
- } catch (RemoteException e) {
- Log.e(TAG, "listener.onCableStatusChanged failed.");
- }
- }
- }
-
- /**
- * Return the active status of the device.
- *
- * @return true if the device is the active source among the connected
- * HDMI-CEC-enabled devices; otherwise false.
- */
- public boolean isActiveSource() {
- return mIsActiveSource;
- }
-
- /**
- * Update the active source state of the device.
- */
- public void setIsActiveSource(boolean state) {
- mIsActiveSource = state;
- }
-
- /**
- * Send <Active Source> command. The default implementation does nothing. Should be
- * overriden by subclass.
- */
- public void sendActiveSource(int physicalAddress) {
- logWarning("<Active Source> not valid for the device type: " + mType
- + " address:" + physicalAddress);
- }
-
- /**
- * Send <Inactive Source> command. The default implementation does nothing. Should be
- * overriden by subclass.
- */
- public void sendInactiveSource(int physicalAddress) {
- logWarning("<Inactive Source> not valid for the device type: " + mType
- + " address:" + physicalAddress);
- }
-
- /**
- * Send <Image View On> command. The default implementation does nothing. Should be
- * overriden by subclass.
- */
- public void sendImageViewOn() {
- logWarning("<Image View On> not valid for the device type: " + mType);
- }
-
- /**
- * Send <Text View On> command. The default implementation does nothing. Should be
- * overriden by subclass.
- */
- public void sendTextViewOn() {
- logWarning("<Text View On> not valid for the device type: " + mType);
- }
-
- /**
- * Check if the connected sink device is in powered-on state. The default implementation
- * simply returns false. Should be overriden by subclass to report the correct state.
- */
- public boolean isSinkDeviceOn() {
- logWarning("isSinkDeviceOn() not valid for the device type: " + mType);
- return false;
- }
-
- private void logWarning(String msg) {
- Log.w(TAG, msg);
- }
-}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java
deleted file mode 100644
index f8cf11d..0000000
--- a/services/core/java/com/android/server/hdmi/HdmiCecDevicePlayback.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.hdmi;
-
-import android.hardware.hdmi.HdmiCec;
-
-/**
- * Class for the logical device of playback type. Devices such as DVD/Blueray player
- * that support 'playback' feature are classified as playback device. It is common
- * that they don't have built-in display, therefore need to talk, stream their contents
- * to TV/display device which is connected through HDMI cable.
- *
- * <p>It closely monitors the status of display device (other devices can be of interest
- * too, but with much less priority), declares itself as 'active source' to have
- * display show its output, switch the source state as ordered by display that may be
- * talking to many other devices connected to it. It also receives commands from display
- * such as remote control signal, standby, status report, playback mode.
- *
- * <p>Declared as package-private, accessed by HdmiCecService only.
- */
-final class HdmiCecDevicePlayback extends HdmiCecDevice {
- private static final String TAG = "HdmiCecDevicePlayback";
-
- private int mSinkDevicePowerStatus;
-
- /**
- * Constructor.
- */
- public HdmiCecDevicePlayback(HdmiCecService service, int type) {
- super(service, type);
- mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_UNKNOWN;
- }
-
- @Override
- public void initialize() {
- // Playback device tries to obtain the power status of TV/display when created,
- // and maintains it all through its lifecycle. CEC spec says there is
- // a maximum 1 second response time. Therefore it should be kept in mind
- // that there can be as much amount of period of time the power status
- // of the display remains unknown after the query is sent out.
- queryTvPowerStatus();
- }
-
- private void queryTvPowerStatus() {
- getService().sendMessage(getType(), HdmiCec.ADDR_TV,
- HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS, HdmiCecService.EMPTY_PARAM);
- }
-
- @Override
- public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
- // Updates power status of display. The cases are:
- // 1) Response for the queried power status request arrives. Update the status.
- // 2) Broadcast or direct <Standby> command from TV, which is sent as TV itself is going
- // into standby mode too.
- if (opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS) {
- mSinkDevicePowerStatus = params[0];
- } else if (srcAddress == HdmiCec.ADDR_TV) {
- if (opcode == HdmiCec.MESSAGE_STANDBY) {
- mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_STANDBY;
- }
- }
- super.handleMessage(srcAddress, dstAddress, opcode, params);
- }
-
- @Override
- public void handleHotplug(boolean connected) {
- // If cable get disconnected sink device becomes unreachable. Switch the status
- // to unknown, and query the status once the cable gets connected back.
- if (!connected) {
- mSinkDevicePowerStatus = HdmiCec.POWER_STATUS_UNKNOWN;
- } else {
- queryTvPowerStatus();
- }
- super.handleHotplug(connected);
- }
-
- @Override
- public boolean isSinkDeviceOn() {
- return mSinkDevicePowerStatus == HdmiCec.POWER_STATUS_ON;
- }
-
- @Override
- public void sendActiveSource(int physicalAddress) {
- setIsActiveSource(true);
- byte[] param = new byte[] {
- (byte) ((physicalAddress >> 8) & 0xff),
- (byte) (physicalAddress & 0xff)
- };
- getService().sendMessage(getType(), HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_ACTIVE_SOURCE,
- param);
- }
-
- @Override
- public void sendInactiveSource(int physicalAddress) {
- setIsActiveSource(false);
- byte[] param = new byte[] {
- (byte) ((physicalAddress >> 8) & 0xff),
- (byte) (physicalAddress & 0xff)
- };
- getService().sendMessage(getType(), HdmiCec.ADDR_TV, HdmiCec.MESSAGE_INACTIVE_SOURCE,
- param);
- }
-
- @Override
- public void sendImageViewOn() {
- getService().sendMessage(getType(), HdmiCec.ADDR_TV, HdmiCec.MESSAGE_IMAGE_VIEW_ON,
- HdmiCecService.EMPTY_PARAM);
- }
-
- @Override
- public void sendTextViewOn() {
- getService().sendMessage(getType(), HdmiCec.ADDR_TV, HdmiCec.MESSAGE_TEXT_VIEW_ON,
- HdmiCecService.EMPTY_PARAM);
- }
-}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java
deleted file mode 100644
index 09ff3ca..0000000
--- a/services/core/java/com/android/server/hdmi/HdmiCecDeviceTv.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.hdmi;
-
-/**
- * Class for logical device of TV type.
- */
-final class HdmiCecDeviceTv extends HdmiCecDevice {
- private static final String TAG = "HdmiCecDeviceTv";
-
- /**
- * Constructor.
- */
- public HdmiCecDeviceTv(HdmiCecService service, int type) {
- super(service, type);
- }
-
- public void initialize() {
- // TODO: Do the initialization task for TV device here.
- }
-}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java
new file mode 100644
index 0000000..ebb6f50
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycodeTranslator.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.view.KeyEvent;
+
+/**
+ * Helper class to translate android keycode to hdmi cec keycode and vice versa.
+ */
+public class HdmiCecKeycodeTranslator {
+ public static final int UNSUPPORTED_KEYCODE = -1;
+ public static final int NO_PARAM = -1;
+
+ // =========================================================================
+ // Hdmi CEC keycodes
+ public static final int CEC_KEYCODE_SELECT = 0x00;
+ public static final int CEC_KEYCODE_UP = 0x01;
+ public static final int CEC_KEYCODE_DOWN = 0x02;
+ public static final int CEC_KEYCODE_LEFT = 0x03;
+ public static final int CEC_KEYCODE_RIGHT = 0x04;
+ public static final int CEC_KEYCODE_RIGHT_UP = 0x05;
+ public static final int CEC_KEYCODE_RIGHT_DOWN = 0x06;
+ public static final int CEC_KEYCODE_LEFT_UP = 0x07;
+ public static final int CEC_KEYCODE_LEFT_DOWN = 0x08;
+ public static final int CEC_KEYCODE_ROOT_MENU = 0x09;
+ public static final int CEC_KEYCODE_SETUP_MENU = 0x0A;
+ public static final int CEC_KEYCODE_CONTENTS_MENU = 0x0B;
+ public static final int CEC_KEYCODE_FAVORITE_MENU = 0x0C;
+ public static final int CEC_KEYCODE_EXIT = 0x0D;
+ // RESERVED = 0x0E - 0x0F
+ public static final int CEC_KEYCODE_MEDIA_TOP_MENU = 0x10;
+ public static final int CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU = 0x11;
+ // RESERVED = 0x12 – 0x1C
+ public static final int CEC_KEYCODE_NUMBER_ENTRY_MODE = 0x1D;
+ public static final int CEC_KEYCODE_NUMBER_11 = 0x1E;
+ public static final int CEC_KEYCODE_NUMBER_12 = 0x1F;
+ public static final int CEC_KEYCODE_NUMBER_0_OR_NUMBER_10 = 0x20;
+ public static final int CEC_KEYCODE_NUMBERS_1 = 0x21;
+ public static final int CEC_KEYCODE_NUMBERS_2 = 0x22;
+ public static final int CEC_KEYCODE_NUMBERS_3 = 0x23;
+ public static final int CEC_KEYCODE_NUMBERS_4 = 0x24;
+ public static final int CEC_KEYCODE_NUMBERS_5 = 0x25;
+ public static final int CEC_KEYCODE_NUMBERS_6 = 0x26;
+ public static final int CEC_KEYCODE_NUMBERS_7 = 0x27;
+ public static final int CEC_KEYCODE_NUMBERS_8 = 0x28;
+ public static final int CEC_KEYCODE_NUMBERS_9 = 0x29;
+ public static final int CEC_KEYCODE_DOT = 0x2A;
+ public static final int CEC_KEYCODE_ENTER = 0x2B;
+ public static final int CEC_KEYCODE_CLEAR = 0x2C;
+ // RESERVED = 0x2D - 0x2E
+ public static final int CEC_KEYCODE_NEXT_FAVORITE = 0x2F;
+ public static final int CEC_KEYCODE_CHANNEL_UP = 0x30;
+ public static final int CEC_KEYCODE_CHANNEL_DOWN = 0x31;
+ public static final int CEC_KEYCODE_PREVIOUS_CHANNEL = 0x32;
+ public static final int CEC_KEYCODE_SOUND_SELECT = 0x33;
+ public static final int CEC_KEYCODE_INPUT_SELECT = 0x34;
+ public static final int CEC_KEYCODE_DISPLAY_INFORMATION = 0x35;
+ public static final int CEC_KEYCODE_HELP = 0x36;
+ public static final int CEC_KEYCODE_PAGE_UP = 0x37;
+ public static final int CEC_KEYCODE_PAGE_DOWN = 0x38;
+ // RESERVED = 0x39 - 0x3F
+ public static final int CEC_KEYCODE_POWER = 0x40;
+ public static final int CEC_KEYCODE_VOLUME_UP = 0x41;
+ public static final int CEC_KEYCODE_VOLUME_DOWN = 0x42;
+ public static final int CEC_KEYCODE_MUTE = 0x43;
+ public static final int CEC_KEYCODE_PLAY = 0x44;
+ public static final int CEC_KEYCODE_STOP = 0x45;
+ public static final int CEC_KEYCODE_PAUSE = 0x46;
+ public static final int CEC_KEYCODE_RECORD = 0x47;
+ public static final int CEC_KEYCODE_REWIND = 0x48;
+ public static final int CEC_KEYCODE_FAST_FORWARD = 0x49;
+ public static final int CEC_KEYCODE_EJECT = 0x4A;
+ public static final int CEC_KEYCODE_FORWARD = 0x4B;
+ public static final int CEC_KEYCODE_BACKWARD = 0x4C;
+ public static final int CEC_KEYCODE_STOP_RECORD = 0x4D;
+ public static final int CEC_KEYCODE_PAUSE_RECORD = 0x4E;
+ public static final int CEC_KEYCODE_RESERVED = 0x4F;
+ public static final int CEC_KEYCODE_ANGLE = 0x50;
+ public static final int CEC_KEYCODE_SUB_PICTURE = 0x51;
+ public static final int CEC_KEYCODE_VIDEO_ON_DEMAND = 0x52;
+ public static final int CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE = 0x53;
+ public static final int CEC_KEYCODE_TIMER_PROGRAMMING = 0x54;
+ public static final int CEC_KEYCODE_INITIAL_CONFIGURATION = 0x55;
+ public static final int CEC_KEYCODE_SELECT_BROADCAST_TYPE = 0x56;
+ public static final int CEC_KEYCODE_SELECT_SOUND_PRESENTATION = 0x57;
+ // RESERVED = 0x58-0x5F
+ public static final int CEC_KEYCODE_PLAY_FUNCTION = 0x60;
+ public static final int CEC_KEYCODE_PAUSE_PLAY_FUNCTION = 0x61;
+ public static final int CEC_KEYCODE_RECORD_FUNCTION = 0x62;
+ public static final int CEC_KEYCODE_PAUSE_RECORD_FUNCTION = 0x63;
+ public static final int CEC_KEYCODE_STOP_FUNCTION = 0x64;
+ public static final int CEC_KEYCODE_MUTE_FUNCTION = 0x65;
+ public static final int CEC_KEYCODE_RESTORE_VOLUME_FUNCTION = 0x66;
+ public static final int CEC_KEYCODE_TUNE_FUNCTION = 0x67;
+ public static final int CEC_KEYCODE_SELECT_MEDIA_FUNCTION = 0x68;
+ public static final int CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION = 0x69;
+ public static final int CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION = 0x6A;
+ public static final int CEC_KEYCODE_POWER_TOGGLE_FUNCTION = 0x6B;
+ public static final int CEC_KEYCODE_POWER_OFF_FUNCTION = 0x6C;
+ public static final int CEC_KEYCODE_POWER_ON_FUNCTION = 0x6D;
+ // RESERVED = 0x6E-0x70
+ public static final int CEC_KEYCODE_F1_BLUE = 0x71;
+ public static final int CEC_KEYCODE_F2_RED = 0x72;
+ public static final int CEC_KEYCODE_F3_GREEN = 0x73;
+ public static final int CEC_KEYCODE_F4_YELLOW = 0x74;
+ public static final int CEC_KEYCODE_F5 = 0x75;
+ public static final int CEC_KEYCODE_DATA = 0x76;
+ // RESERVED = 0x77-0xFF
+
+ // =========================================================================
+ // UI Broadcast Type
+ public static final int UI_BROADCAST_TOGGLE_ALL = 0x00;
+ public static final int UI_BROADCAST_TOGGLE_ANALOGUE_DIGITAL = 0x01;
+ public static final int UI_BROADCAST_ANALOGUE = 0x10;
+ public static final int UI_BROADCAST_ANALOGUE_TERRESTRIAL = 0x20;
+ public static final int UI_BROADCAST_ANALOGUE_CABLE = 0x30;
+ public static final int UI_BROADCAST_ANALOGUE_SATELLITE = 0x40;
+ public static final int UI_BROADCAST_DIGITAL = 0x50;
+ public static final int UI_BROADCAST_DIGITAL_TERRESTRIAL = 0x60;
+ public static final int UI_BROADCAST_DIGITAL_CABLE = 0x70;
+ public static final int UI_BROADCAST_DIGITAL_SATELLITE = 0x80;
+ public static final int UI_BROADCAST_DIGITAL_COMMNICATIONS_SATELLITE = 0x90;
+ public static final int UI_BROADCAST_DIGITAL_COMMNICATIONS_SATELLITE_2 = 0x91;
+ public static final int UI_BROADCAST_IP = 0xA0;
+
+ // =========================================================================
+ // UI Sound Presentation Control
+ public static final int UI_SOUND_PRESENTATION_SOUND_MIX_DUAL_MONO = 0x20;
+ public static final int UI_SOUND_PRESENTATION_SOUND_MIX_KARAOKE = 0x30;
+ public static final int UI_SOUND_PRESENTATION_SELECT_AUDIO_DOWN_MIX = 0x80;
+ public static final int UI_SOUND_PRESENTATION_SELECT_AUDIO_AUTO_REVERBERATION = 0x90;
+ public static final int UI_SOUND_PRESENTATION_SELECT_AUDIO_AUTO_EQUALIZER = 0xA0;
+ public static final int UI_SOUND_PRESENTATION_BASS_STEP_PLUS = 0xB1;
+ public static final int UI_SOUND_PRESENTATION_BASS_NEUTRAL = 0xB2;
+ public static final int UI_SOUND_PRESENTATION_BASS_STEP_MINUS = 0xB3;
+ public static final int UI_SOUND_PRESENTATION_TREBLE_STEP_PLUS = 0xC1;
+ public static final int UI_SOUND_PRESENTATION_TREBLE_NEUTRAL = 0xC2;
+ public static final int UI_SOUND_PRESENTATION_TREBLE_STEP_MINUS = 0xC3;
+
+ private HdmiCecKeycodeTranslator() {
+ }
+
+ /**
+ * A mapping between andorid and cec keycode.
+ *
+ * <p>Normal implementation of this looks like
+ * <pre>
+ * new KeycodeEntry(KeyEvent.KEYCODE_DPAD_CENTER, CEC_KEYCODE_SELECT);
+ * </pre>
+ * <p>However, some keys in CEC requires additional parameter.
+ * In order to use parameterized cec key, add unique android keycode (existing or custom)
+ * corresponding to a pair of cec keycode and and its param.
+ * <pre>
+ * new KeycodeEntry(CUSTOME_ANDORID_KEY_1, CEC_KEYCODE_SELECT_BROADCAST_TYPE,
+ * UI_BROADCAST_TOGGLE_ALL);
+ * new KeycodeEntry(CUSTOME_ANDORID_KEY_2, CEC_KEYCODE_SELECT_BROADCAST_TYPE,
+ * UI_BROADCAST_ANALOGUE);
+ * </pre>
+ */
+ private static class KeycodeEntry {
+ private final int mAndroidKeycode;
+ private final int mCecKeycode;
+ private final int mParam;
+
+ private KeycodeEntry(int androidKeycode, int cecKeycode, int param) {
+ this.mAndroidKeycode = androidKeycode;
+ this.mCecKeycode = cecKeycode;
+ this.mParam = param;
+ }
+
+ private KeycodeEntry(int androidKeycode, int cecKeycode) {
+ this(androidKeycode, cecKeycode, NO_PARAM);
+ }
+
+ private byte[] toCecKeycodeIfMatched(int androidKeycode) {
+ if (mAndroidKeycode == androidKeycode) {
+ if (mParam == NO_PARAM) {
+ return new byte[] {
+ (byte) (mCecKeycode & 0xFF)
+ };
+ } else {
+ return new byte[] {
+ (byte) (mCecKeycode & 0xFF),
+ (byte) (mParam & 0xFF)
+ };
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private int toAndroidKeycodeIfMatched(int cecKeycode, int param) {
+ if (cecKeycode == mCecKeycode && mParam == param) {
+ return mAndroidKeycode;
+ } else {
+ return UNSUPPORTED_KEYCODE;
+ }
+ }
+ }
+
+ // Keycode entry container for all mappings.
+ // Note that order of entry is the same as above cec keycode definition.
+ private static final KeycodeEntry[] KEYCODE_ENTRIES = new KeycodeEntry[] {
+ new KeycodeEntry(KeyEvent.KEYCODE_DPAD_CENTER, CEC_KEYCODE_SELECT),
+ new KeycodeEntry(KeyEvent.KEYCODE_DPAD_UP, CEC_KEYCODE_UP),
+ new KeycodeEntry(KeyEvent.KEYCODE_DPAD_DOWN, CEC_KEYCODE_DOWN),
+ new KeycodeEntry(KeyEvent.KEYCODE_DPAD_LEFT, CEC_KEYCODE_LEFT),
+ new KeycodeEntry(KeyEvent.KEYCODE_DPAD_RIGHT, CEC_KEYCODE_RIGHT),
+ // No Android keycode defined for CEC_KEYCODE_RIGHT_UP
+ // No Android keycode defined for CEC_KEYCODE_RIGHT_DOWN
+ // No Android keycode defined for CEC_KEYCODE_LEFT_UP
+ // No Android keycode defined for CEC_KEYCODE_LEFT_DOWN
+ new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU),
+ new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU),
+ new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_CONTENTS_MENU),
+ // No Android keycode defined for CEC_KEYCODE_FAVORITE_MENU
+ new KeycodeEntry(KeyEvent.KEYCODE_BACK, CEC_KEYCODE_EXIT),
+ // RESERVED
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_TOP_MENU, CEC_KEYCODE_MEDIA_TOP_MENU),
+ // No Android keycode defined for CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU
+ // RESERVED
+ // No Android keycode defined for CEC_KEYCODE_NUMBER_ENTRY_MODE
+ new KeycodeEntry(KeyEvent.KEYCODE_11, CEC_KEYCODE_NUMBER_11),
+ new KeycodeEntry(KeyEvent.KEYCODE_12, CEC_KEYCODE_NUMBER_12),
+ new KeycodeEntry(KeyEvent.KEYCODE_0, CEC_KEYCODE_NUMBER_0_OR_NUMBER_10),
+ new KeycodeEntry(KeyEvent.KEYCODE_1, CEC_KEYCODE_NUMBERS_1),
+ new KeycodeEntry(KeyEvent.KEYCODE_2, CEC_KEYCODE_NUMBERS_2),
+ new KeycodeEntry(KeyEvent.KEYCODE_3, CEC_KEYCODE_NUMBERS_3),
+ new KeycodeEntry(KeyEvent.KEYCODE_4, CEC_KEYCODE_NUMBERS_4),
+ new KeycodeEntry(KeyEvent.KEYCODE_5, CEC_KEYCODE_NUMBERS_5),
+ new KeycodeEntry(KeyEvent.KEYCODE_6, CEC_KEYCODE_NUMBERS_6),
+ new KeycodeEntry(KeyEvent.KEYCODE_7, CEC_KEYCODE_NUMBERS_7),
+ new KeycodeEntry(KeyEvent.KEYCODE_8, CEC_KEYCODE_NUMBERS_8),
+ new KeycodeEntry(KeyEvent.KEYCODE_9, CEC_KEYCODE_NUMBERS_9),
+ new KeycodeEntry(KeyEvent.KEYCODE_PERIOD, CEC_KEYCODE_DOT),
+ new KeycodeEntry(KeyEvent.KEYCODE_NUMPAD_ENTER, CEC_KEYCODE_ENTER),
+ new KeycodeEntry(KeyEvent.KEYCODE_CLEAR, CEC_KEYCODE_CLEAR),
+ // RESERVED
+ // No Android keycode defined for CEC_KEYCODE_NEXT_FAVORITE
+ new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP),
+ new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN),
+ new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL),
+ // No Android keycode defined for CEC_KEYCODE_SOUND_SELECT
+ new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT),
+ new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION),
+ // No Android keycode defined for CEC_KEYCODE_HELP
+ new KeycodeEntry(KeyEvent.KEYCODE_PAGE_UP, CEC_KEYCODE_PAGE_UP),
+ new KeycodeEntry(KeyEvent.KEYCODE_PAGE_DOWN, CEC_KEYCODE_PAGE_DOWN),
+ // RESERVED
+ new KeycodeEntry(KeyEvent.KEYCODE_POWER, CEC_KEYCODE_POWER),
+ new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_UP, CEC_KEYCODE_VOLUME_UP),
+ new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_DOWN, CEC_KEYCODE_VOLUME_DOWN),
+ new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_MUTE, CEC_KEYCODE_MUTE),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PLAY, CEC_KEYCODE_PLAY),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_STOP, CEC_KEYCODE_STOP),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PAUSE, CEC_KEYCODE_PAUSE),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_RECORD, CEC_KEYCODE_RECORD),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_REWIND, CEC_KEYCODE_REWIND),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, CEC_KEYCODE_FAST_FORWARD),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_EJECT, CEC_KEYCODE_EJECT),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_NEXT, CEC_KEYCODE_FORWARD),
+ new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PREVIOUS, CEC_KEYCODE_BACKWARD),
+ // No Android keycode defined for CEC_KEYCODE_STOP_RECORD
+ // No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD
+ // No Android keycode defined for CEC_KEYCODE_RESERVED
+ // No Android keycode defined for CEC_KEYCODE_ANGLE
+ // No Android keycode defined for CEC_KEYCODE_SUB_PICTURE
+ // No Android keycode defined for CEC_KEYCODE_VIDEO_ON_DEMAND
+ new KeycodeEntry(KeyEvent.KEYCODE_GUIDE, CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE),
+ // No Android keycode defined for CEC_KEYCODE_TIMER_PROGRAMMING
+ // No Android keycode defined for CEC_KEYCODE_INITIAL_CONFIGURATION
+ // No Android keycode defined for CEC_KEYCODE_SELECT_BROADCAST_TYPE
+ // No Android keycode defined for CEC_KEYCODE_SELECT_SOUND_PRESENTATION
+ // RESERVED
+ // The following deterministic key definitions do not need key mapping
+ // since they are supposed to be generated programmatically only.
+ // No Android keycode defined for CEC_KEYCODE_PLAY_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_PAUSE_PLAY_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_RECORD_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_STOP_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_MUTE_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_RESTORE_VOLUME_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_TUNE_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_SELECT_MEDIA_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_POWER_TOGGLE_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_POWER_OFF_FUNCTION
+ // No Android keycode defined for CEC_KEYCODE_POWER_ON_FUNCTION
+ // RESERVED
+ new KeycodeEntry(KeyEvent.KEYCODE_PROG_BLUE, CEC_KEYCODE_F1_BLUE),
+ new KeycodeEntry(KeyEvent.KEYCODE_PROG_RED, CEC_KEYCODE_F2_RED),
+ new KeycodeEntry(KeyEvent.KEYCODE_PROG_GREEN, CEC_KEYCODE_F3_GREEN),
+ new KeycodeEntry(KeyEvent.KEYCODE_PROG_YELLOW, CEC_KEYCODE_F4_YELLOW),
+ new KeycodeEntry(KeyEvent.KEYCODE_F5, CEC_KEYCODE_F5),
+ new KeycodeEntry(KeyEvent.KEYCODE_TV_DATA_SERVICE, CEC_KEYCODE_DATA),
+ // RESERVED
+ // Add a new key mapping here if new keycode is introduced.
+ };
+
+ /**
+ * Translate Android keycode to Hdmi Cec keycode.
+ *
+ * @param keycode Android keycode. For details, refer {@link KeyEvent}
+ * @return array of byte which contains cec keycode and param if it has;
+ * return null if failed to find matched cec keycode
+ */
+ static byte[] androidKeyToCecKey(int keycode) {
+ for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) {
+ byte[] cecKeycode = KEYCODE_ENTRIES[i].toCecKeycodeIfMatched(keycode);
+ if (cecKeycode != null) {
+ return cecKeycode;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Translate Hdmi CEC keycode to Android keycode.
+ *
+ * @param keycode Cec keycode. If has no param, put {@link #NO_PARAM}
+ * @return cec keycode corresponding to the given android keycode.
+ * If finds no matched keycode, return {@link #UNSUPPORTED_KEYCODE}
+ */
+ static int cecKeyToAndroidKey(int keycode, int param) {
+ for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) {
+ int androidKey = KEYCODE_ENTRIES[i].toAndroidKeycodeIfMatched(keycode, param);
+ if (androidKey != UNSUPPORTED_KEYCODE) {
+ return androidKey;
+ }
+ }
+ return UNSUPPORTED_KEYCODE;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index aac2a15..6697a53 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -16,95 +16,132 @@
package com.android.server.hdmi;
-import com.android.server.hdmi.HdmiCecController.AllocateLogicalAddressCallback;
-
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
/**
* Class that models a logical CEC device hosted in this system. Handles initialization,
* CEC commands that call for actions customized per device type.
*/
abstract class HdmiCecLocalDevice {
+ private static final String TAG = "HdmiCecLocalDevice";
- protected final HdmiCecController mController;
+ protected final HdmiControlService mService;
protected final int mDeviceType;
- protected final AddressAllocationCallback mAllocationCallback;
protected int mAddress;
protected int mPreferredAddress;
protected HdmiCecDeviceInfo mDeviceInfo;
- /**
- * Callback interface to notify newly allocated logical address of the given
- * local device.
- */
- interface AddressAllocationCallback {
- /**
- * Called when a logical address of the given device is allocated.
- *
- * @param deviceType original device type
- * @param logicalAddress newly allocated logical address
- */
- void onAddressAllocated(int deviceType, int logicalAddress);
- }
-
- protected HdmiCecLocalDevice(HdmiCecController controller, int deviceType,
- AddressAllocationCallback callback) {
- mController = controller;
+ protected HdmiCecLocalDevice(HdmiControlService service, int deviceType) {
+ mService = service;
mDeviceType = deviceType;
- mAllocationCallback = callback;
mAddress = HdmiCec.ADDR_UNREGISTERED;
}
// Factory method that returns HdmiCecLocalDevice of corresponding type.
- static HdmiCecLocalDevice create(HdmiCecController controller, int deviceType,
- AddressAllocationCallback callback) {
+ static HdmiCecLocalDevice create(HdmiControlService service, int deviceType) {
switch (deviceType) {
case HdmiCec.DEVICE_TV:
- return new HdmiCecLocalDeviceTv(controller, callback);
+ return new HdmiCecLocalDeviceTv(service);
case HdmiCec.DEVICE_PLAYBACK:
- return new HdmiCecLocalDevicePlayback(controller, callback);
+ return new HdmiCecLocalDevicePlayback(service);
default:
return null;
}
}
- abstract void init();
+ void init() {
+ mPreferredAddress = HdmiCec.ADDR_UNREGISTERED;
+ // TODO: load preferred address from permanent storage.
+ }
/**
- * Called when a logical address of the local device is allocated.
- * Note that internal variables are updated before it's called.
+ * Called once a logical address of the local device is allocated.
*/
protected abstract void onAddressAllocated(int logicalAddress);
- protected void allocateAddress(int type) {
- mController.allocateLogicalAddress(type, mPreferredAddress,
- new AllocateLogicalAddressCallback() {
- @Override
- public void onAllocated(int deviceType, int logicalAddress) {
- mAddress = mPreferredAddress = logicalAddress;
-
- // Create and set device info.
- HdmiCecDeviceInfo deviceInfo = createDeviceInfo(mAddress, deviceType);
- setDeviceInfo(deviceInfo);
- mController.addDeviceInfo(deviceInfo);
-
- mController.addLogicalAddress(logicalAddress);
- onAddressAllocated(logicalAddress);
- if (mAllocationCallback != null) {
- mAllocationCallback.onAddressAllocated(deviceType, logicalAddress);
- }
- }
- });
+ /**
+ * Dispatch incoming message.
+ *
+ * @param message incoming message
+ * @return true if consumed a message; otherwise, return false.
+ */
+ final boolean dispatchMessage(HdmiCecMessage message) {
+ int dest = message.getDestination();
+ if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) {
+ return false;
+ }
+ return onMessage(message);
}
- private final HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
- int vendorId = mController.getVendorId();
- int physicalAddress = mController.getPhysicalAddress();
- // TODO: get device name read from system configuration.
- String displayName = HdmiCec.getDefaultDeviceName(logicalAddress);
- return new HdmiCecDeviceInfo(logicalAddress,
- physicalAddress, deviceType, vendorId, displayName);
+ protected boolean onMessage(HdmiCecMessage message) {
+ switch (message.getOpcode()) {
+ case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
+ return handleGetMenuLanguage(message);
+ case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
+ return handleGivePhysicalAddress();
+ case HdmiCec.MESSAGE_GIVE_OSD_NAME:
+ return handleGiveOsdName(message);
+ case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID:
+ return handleGiveDeviceVendorId();
+ case HdmiCec.MESSAGE_GET_CEC_VERSION:
+ return handleGetCecVersion(message);
+ default:
+ return false;
+ }
+ }
+
+ protected boolean handleGivePhysicalAddress() {
+ int physicalAddress = mService.getPhysicalAddress();
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, physicalAddress, mDeviceType);
+ mService.sendCecCommand(cecMessage);
+ return true;
+ }
+
+ protected boolean handleGiveDeviceVendorId() {
+ int vendorId = mService.getVendorId();
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ mAddress, vendorId);
+ mService.sendCecCommand(cecMessage);
+ return true;
+ }
+
+ protected boolean handleGetCecVersion(HdmiCecMessage message) {
+ int version = mService.getCecVersion();
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
+ message.getSource(), version);
+ mService.sendCecCommand(cecMessage);
+ return true;
+ }
+
+ protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
+ message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
+ HdmiConstants.ABORT_UNRECOGNIZED_MODE));
+ return true;
+ }
+
+ protected boolean handleGiveOsdName(HdmiCecMessage message) {
+ // Note that since this method is called after logical address allocation is done,
+ // mDeviceInfo should not be null.
+ HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
+ mAddress, message.getSource(), mDeviceInfo.getDisplayName());
+ if (cecMessage != null) {
+ mService.sendCecCommand(cecMessage);
+ } else {
+ Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName());
+ }
+ return true;
+ }
+
+ final void handleAddressAllocated(int logicalAddress) {
+ mAddress = mPreferredAddress = logicalAddress;
+ onAddressAllocated(logicalAddress);
}
HdmiCecDeviceInfo getDeviceInfo() {
@@ -128,4 +165,8 @@
void setPreferredAddress(int addr) {
mPreferredAddress = addr;
}
+
+ int getPreferredAddress() {
+ return mPreferredAddress;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 3347725..d79e283 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -23,18 +23,13 @@
*/
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
- HdmiCecLocalDevicePlayback(HdmiCecController controller, AddressAllocationCallback callback) {
- super(controller, HdmiCec.DEVICE_PLAYBACK, callback);
- }
-
- @Override
- void init() {
- allocateAddress(mDeviceType);
+ HdmiCecLocalDevicePlayback(HdmiControlService service) {
+ super(service, HdmiCec.DEVICE_PLAYBACK);
}
@Override
protected void onAddressAllocated(int logicalAddress) {
- mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, mController.getPhysicalAddress(), mDeviceType));
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mService.getPhysicalAddress(), mDeviceType));
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 93761ab..6394fe7 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -16,31 +16,98 @@
package com.android.server.hdmi;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Locale;
/**
* Represent a logical device of type TV residing in Android system.
*/
final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
+ private static final String TAG = "HdmiCecLocalDeviceTv";
- HdmiCecLocalDeviceTv(HdmiCecController controller, AddressAllocationCallback callback) {
- super(controller, HdmiCec.DEVICE_TV, callback);
- }
-
- @Override
- void init() {
- allocateAddress(mDeviceType);
+ HdmiCecLocalDeviceTv(HdmiControlService service) {
+ super(service, HdmiCec.DEVICE_TV);
}
@Override
protected void onAddressAllocated(int logicalAddress) {
// TODO: vendor-specific initialization here.
- mController.sendCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- mAddress, mController.getPhysicalAddress(), mDeviceType));
- mController.sendCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
- mAddress, mController.getVendorId()));
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ mAddress, mService.getPhysicalAddress(), mDeviceType));
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
+ mAddress, mService.getVendorId()));
+ mService.launchDeviceDiscovery(mAddress);
// TODO: Start routing control action, device discovery action.
}
+
+ @Override
+ protected boolean onMessage(HdmiCecMessage message) {
+ switch (message.getOpcode()) {
+ case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
+ return handleReportPhysicalAddress(message);
+ default:
+ return super.onMessage(message);
+ }
+ }
+
+ /**
+ * Performs the action 'device select', or 'one touch play' initiated by TV.
+ *
+ * @param targetAddress logical address of the device to select
+ * @param callback callback object to report the result with
+ */
+ void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
+ HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
+ if (targetDevice == null) {
+ invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
+ return;
+ }
+ mService.removeAction(DeviceSelectAction.class);
+ mService.addAndStartAction(new DeviceSelectAction(mService, mAddress,
+ mService.getPhysicalAddress(), targetDevice, callback));
+ }
+
+ private static void invokeCallback(IHdmiControlCallback callback, int result) {
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
+
+ @Override
+ protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
+ HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
+ mAddress, Locale.getDefault().getISO3Language());
+ // TODO: figure out how to handle failed to get language code.
+ if (command != null) {
+ mService.sendCecCommand(command);
+ } else {
+ Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
+ }
+ return true;
+ }
+
+ private boolean handleReportPhysicalAddress(HdmiCecMessage message) {
+ // Ignore if [Device Discovery Action] is going on.
+ if (mService.hasAction(DeviceDiscoveryAction.class)) {
+ Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
+ + "because Device Discovery Action is on-going:" + message);
+ return true;
+ }
+
+ int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+ mService.addAndStartAction(new NewDeviceAction(mService,
+ mAddress, message.getSource(), physicalAddress));
+
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 6c2be34..8dbfd85 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -270,6 +270,38 @@
}
/**
+ * Build <Set Stream Path> command.
+ *
+ * <p>This is a broadcast message sent to all devices on the bus.
+ *
+ * @param src source address of command
+ * @param streamPath physical address of the device to start streaming
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildSetStreamPath(int src, int streamPath) {
+ return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_SET_STREAM_PATH,
+ physicalAddressToParam(streamPath));
+ }
+
+ /**
+ * Build <Routing Change> command.
+ *
+ * <p>This is a broadcast message sent to all devices on the bus.
+ *
+ * @param src source address of command
+ * @param oldPath physical address of the currently active routing path
+ * @param newPath physical address of the new active routing path
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRoutingChange(int src, int oldPath, int newPath) {
+ byte[] param = new byte[] {
+ (byte) ((oldPath >> 8) & 0xFF), (byte) (oldPath & 0xFF),
+ (byte) ((newPath >> 8) & 0xFF), (byte) (newPath & 0xFF)
+ };
+ return buildCommand(src, HdmiCec.ADDR_BROADCAST, HdmiCec.MESSAGE_ROUTING_CHANGE, param);
+ }
+
+ /**
* Build <Give Device Power Status> command.
*
* @param src source address of command
@@ -319,10 +351,19 @@
* @return newly created {@link HdmiCecMessage}
*/
static HdmiCecMessage buildUserControlPressed(int src, int dest, int uiCommand) {
- byte[] params = new byte[] {
- (byte) uiCommand
- };
- return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, params);
+ return buildUserControlPressed(src, dest, new byte[] { (byte) uiCommand });
+ }
+
+ /**
+ * Build <User Control Pressed> command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param commandParam uiCommand and the additional parameter
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildUserControlPressed(int src, int dest, byte[] commandParam) {
+ return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_PRESSED, commandParam);
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageCache.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageCache.java
new file mode 100644
index 0000000..abda656
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageCache.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.FastImmutableArraySet;
+import android.util.SparseArray;
+
+/**
+ * Cache for incoming message. It caches {@link HdmiCecMessage} with source address and opcode
+ * as a key.
+ *
+ * <p>Note that whenever a device is removed it should call {@link #flushMessagesFrom(int)}
+ * to clean up messages come from the device.
+ */
+final class HdmiCecMessageCache {
+ private static final FastImmutableArraySet<Integer> CACHEABLE_OPCODES = new FastImmutableArraySet<>(
+ new Integer[] {
+ HdmiCec.MESSAGE_SET_OSD_NAME,
+ HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS,
+ HdmiCec.MESSAGE_DEVICE_VENDOR_ID,
+ HdmiCec.MESSAGE_CEC_VERSION,
+ });
+
+ // It's like [Source Logical Address, [Opcode, HdmiCecMessage]].
+ private final SparseArray<SparseArray<HdmiCecMessage>> mCache = new SparseArray<>();
+
+ HdmiCecMessageCache() {
+ }
+
+ /**
+ * Return a {@link HdmiCecMessage} corresponding to the given {@code address} and
+ * {@code opcode}.
+ *
+ * @param address a logical address of source device
+ * @param opcode opcode of message
+ * @return null if has no {@link HdmiCecMessage} matched to the given {@code address} and {code
+ * opcode}
+ */
+ public HdmiCecMessage getMessage(int address, int opcode) {
+ SparseArray<HdmiCecMessage> messages = mCache.get(address);
+ if (messages == null) {
+ return null;
+ }
+
+ return messages.get(opcode);
+ }
+
+ /**
+ * Flush all {@link HdmiCecMessage}s sent from the given {@code address}.
+ *
+ * @param address a logical address of source device
+ */
+ public void flushMessagesFrom(int address) {
+ mCache.remove(address);
+ }
+
+ /**
+ * Flush all cached {@link HdmiCecMessage}s.
+ */
+ public void flushAll() {
+ mCache.clear();
+ }
+
+ /**
+ * Cache incoming {@link HdmiCecMessage}. If opcode of message is not listed on
+ * cacheable opcodes list, just ignore it.
+ *
+ * @param message a {@link HdmiCecMessage} to be cached
+ */
+ public void cacheMessage(HdmiCecMessage message) {
+ int opcode = message.getOpcode();
+ if (!isCacheable(opcode)) {
+ return;
+ }
+
+ int source = message.getSource();
+ SparseArray<HdmiCecMessage> messages = mCache.get(source);
+ if (messages == null) {
+ messages = new SparseArray<>();
+ mCache.put(source, messages);
+ }
+ messages.put(opcode, message);
+ }
+
+ private boolean isCacheable(int opcode) {
+ return CACHEABLE_OPCODES.contains(opcode);
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java
deleted file mode 100644
index 98dc72f..0000000
--- a/services/core/java/com/android/server/hdmi/HdmiCecService.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.hdmi;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.hardware.hdmi.HdmiCec;
-import android.hardware.hdmi.HdmiCecMessage;
-import android.hardware.hdmi.IHdmiCecListener;
-import android.hardware.hdmi.IHdmiCecService;
-import android.os.Binder;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.server.SystemService;
-import libcore.util.EmptyArray;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Locale;
-
-/**
- * Provides a service for sending and processing HDMI-CEC messages, and providing
- * the information on HDMI settings in general.
- */
-public final class HdmiCecService extends SystemService {
- private static final String TAG = "HdmiCecService";
-
- // Maintains the allocated logical devices. Device type, not logical address,
- // is used for key as logical address is likely to change over time while
- // device type is permanent. Type-address mapping is maintained only at
- // native level.
- private final SparseArray<HdmiCecDevice> mLogicalDevices = new SparseArray<HdmiCecDevice>();
-
- // List of IBinder.DeathRecipient instances to handle dead IHdmiCecListener
- // objects.
- private final ArrayList<ListenerRecord> mListenerRecords = new ArrayList<ListenerRecord>();
-
- // Used to synchronize the access to the service.
- private final Object mLock = new Object();
-
- // Stores the pointer to the native implementation of the service that
- // interacts with HAL.
- private long mNativePtr;
-
- private static final String PERMISSION = "android.permission.HDMI_CEC";
-
- static final byte[] EMPTY_PARAM = EmptyArray.BYTE;
-
- public HdmiCecService(Context context) {
- super(context);
- }
-
- private static native long nativeInit(HdmiCecService service);
-
- @Override
- public void onStart() {
- // Stop publishing the service. Soon to be deprecated.
- Log.w(TAG, "In transition to HdmiControlService. May not work.");
- }
-
- /**
- * Called by native when an HDMI-CEC message arrived. Invokes the registered
- * listeners to handle the message.
- */
- private void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
- // TODO: Messages like <Standby> may not need be passed to listener
- // but better be handled in service by turning off the screen
- // or putting the device into suspend mode. List up such messages
- // and handle them here.
- synchronized (mLock) {
- if (dstAddress == HdmiCec.ADDR_BROADCAST) {
- for (int i = 0; i < mLogicalDevices.size(); ++i) {
- mLogicalDevices.valueAt(i).handleMessage(srcAddress, dstAddress, opcode,
- params);
- }
- } else {
- int type = HdmiCec.getTypeFromAddress(dstAddress);
- HdmiCecDevice device = mLogicalDevices.get(type);
- if (device == null) {
- Log.w(TAG, "logical device not found. type: " + type);
- return;
- }
- device.handleMessage(srcAddress, dstAddress, opcode, params);
- }
- }
- }
-
- /**
- * Called by native when internal HDMI hotplug event occurs. Invokes the registered
- * listeners to handle the event.
- */
- private void handleHotplug(boolean connected) {
- synchronized(mLock) {
- for (int i = 0; i < mLogicalDevices.size(); ++i) {
- mLogicalDevices.valueAt(i).handleHotplug(connected);
- }
- }
- }
-
- /**
- * Called by native when it needs to know whether we have an active source.
- * The native part uses the return value to respond to <Request Active
- * Source >.
- *
- * @return type of the device which is active; DEVICE_INACTIVE if there is
- * no active logical device in the system.
- */
- private int getActiveSource() {
- synchronized(mLock) {
- for (int i = 0; i < mLogicalDevices.size(); ++i) {
- if (mLogicalDevices.valueAt(i).isActiveSource()) {
- return mLogicalDevices.keyAt(i);
- }
- }
- }
- return HdmiCec.DEVICE_INACTIVE;
- }
-
- /**
- * Called by native when a request for the menu language of the device was
- * received. The native part uses the return value to generate the message
- * <Set Menu Language> in response. The language should be of
- * the 3-letter format as defined in ISO/FDIS 639-2. We use system default
- * locale.
- */
- private String getLanguage(int type) {
- return Locale.getDefault().getISO3Language();
- }
-
- private void enforceAccessPermission() {
- getContext().enforceCallingOrSelfPermission(PERMISSION, "HdmiCecService");
- }
-
- private void dumpInternal(PrintWriter pw) {
- pw.println("HdmiCecService (dumpsys hdmi_cec)");
- pw.println("");
- synchronized (mLock) {
- for (int i = 0; i < mLogicalDevices.size(); ++i) {
- HdmiCecDevice device = mLogicalDevices.valueAt(i);
- pw.println("Device: type=" + device.getType() +
- ", active=" + device.isActiveSource());
- }
- }
- }
-
- // Remove logical device of a given type.
- private void removeLogicalDeviceLocked(int type) {
- ensureValidType(type);
- mLogicalDevices.remove(type);
- nativeRemoveLogicalAddress(mNativePtr, type);
- }
-
- private static void ensureValidType(int type) {
- if (!HdmiCec.isValidType(type)) {
- throw new IllegalArgumentException("invalid type: " + type);
- }
- }
-
- // Return the logical device identified by the given binder token.
- private HdmiCecDevice getLogicalDeviceLocked(IBinder b) {
- for (int i = 0; i < mLogicalDevices.size(); ++i) {
- HdmiCecDevice device = mLogicalDevices.valueAt(i);
- if (device.getToken() == b) {
- return device;
- }
- }
- throw new IllegalArgumentException("Device not found");
- }
-
- // package-private. Used by HdmiCecDevice and its subclasses only.
- void sendMessage(int type, int address, int opcode, byte[] params) {
- nativeSendMessage(mNativePtr, type, address, opcode, params);
- }
-
- private void setOsdNameLocked(String name) {
- nativeSetOsdName(mNativePtr, name.getBytes(Charset.forName("US-ASCII")));
- }
-
- private final class ListenerRecord implements IBinder.DeathRecipient {
- private final IHdmiCecListener mListener;
- private final int mType;
-
- public ListenerRecord(IHdmiCecListener listener, int type) {
- mListener = listener;
- mType = type;
- }
-
- @Override
- public void binderDied() {
- synchronized (mLock) {
- mListenerRecords.remove(this);
- HdmiCecDevice device = mLogicalDevices.get(mType);
- if (device != null) {
- device.removeListener(mListener);
- if (!device.hasListener()) {
- removeLogicalDeviceLocked(mType);
- }
- }
- }
- }
- }
-
- private final class BinderService extends IHdmiCecService.Stub {
-
- @Override
- public IBinder allocateLogicalDevice(int type, IHdmiCecListener listener) {
- enforceAccessPermission();
- ensureValidType(type);
- if (listener == null) {
- throw new IllegalArgumentException("listener must not be null");
- }
- synchronized (mLock) {
- HdmiCecDevice device = mLogicalDevices.get(type);
- if (device != null) {
- Log.v(TAG, "Logical address already allocated. Adding listener only.");
- } else {
- int address = nativeAllocateLogicalAddress(mNativePtr, type);
- if (!HdmiCec.isValidAddress(address)) {
- Log.e(TAG, "Logical address was not allocated");
- return null;
- } else {
- device = HdmiCecDevice.create(HdmiCecService.this, type);
- if (device == null) {
- Log.e(TAG, "Device type not supported yet.");
- return null;
- }
- device.initialize();
- mLogicalDevices.put(type, device);
- }
- }
-
- // Adds the listener and its monitor
- ListenerRecord record = new ListenerRecord(listener, type);
- try {
- listener.asBinder().linkToDeath(record, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Listener already died");
- if (!device.hasListener()) {
- removeLogicalDeviceLocked(type);
- }
- return null;
- }
- mListenerRecords.add(record);
- device.addListener(listener);
- return device.getToken();
- }
- }
-
- @Override
- public void sendActiveSource(IBinder b) {
- enforceAccessPermission();
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- device.sendActiveSource(nativeGetPhysicalAddress(mNativePtr));
- }
- }
-
- @Override
- public void sendInactiveSource(IBinder b) {
- enforceAccessPermission();
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- device.sendInactiveSource(nativeGetPhysicalAddress(mNativePtr));
- }
- }
-
- @Override
- public void sendImageViewOn(IBinder b) {
- enforceAccessPermission();
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- device.sendImageViewOn();
- }
- }
-
- @Override
- public void sendTextViewOn(IBinder b) {
- enforceAccessPermission();
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- device.sendTextViewOn();
- }
- }
-
- public void sendGiveDevicePowerStatus(IBinder b, int address) {
- enforceAccessPermission();
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- nativeSendMessage(mNativePtr, device.getType(), address,
- HdmiCec.MESSAGE_GIVE_DEVICE_POWER_STATUS, EMPTY_PARAM);
- }
- }
-
- @Override
- public boolean isTvOn(IBinder b) {
- enforceAccessPermission();
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- return device.isSinkDeviceOn();
- }
- }
-
- @Override
- public void removeServiceListener(IBinder b, IHdmiCecListener listener) {
- enforceAccessPermission();
- if (listener == null) {
- throw new IllegalArgumentException("listener must not be null");
- }
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- for (ListenerRecord record : mListenerRecords) {
- if (record.mType == device.getType()
- && record.mListener.asBinder() == listener.asBinder()) {
- mListenerRecords.remove(record);
- device.removeListener(record.mListener);
- if (!device.hasListener()) {
- removeLogicalDeviceLocked(record.mType);
- }
- break;
- }
- }
- }
- }
-
- @Override
- public void sendMessage(IBinder b, HdmiCecMessage message) {
- enforceAccessPermission();
- if (message == null) {
- throw new IllegalArgumentException("message must not be null");
- }
- synchronized (mLock) {
- HdmiCecDevice device = getLogicalDeviceLocked(b);
- nativeSendMessage(mNativePtr, device.getType(), message.getDestination(),
- message.getOpcode(), message.getParams());
- }
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission denial: can't dump HdmiCecService from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
- + " without permission " + android.Manifest.permission.DUMP);
- return;
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- dumpInternal(pw);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
- private static native int nativeAllocateLogicalAddress(long handler, int deviceType);
- private static native void nativeRemoveLogicalAddress(long handler, int deviceType);
- private static native void nativeSendMessage(long handler, int deviceType, int destination,
- int opcode, byte[] params);
- private static native int nativeGetPhysicalAddress(long handler);
- private static native void nativeSetOsdName(long handler, byte[] name);
-}
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
index a83d1ed..8f319ea 100644
--- a/services/core/java/com/android/server/hdmi/HdmiConstants.java
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -39,9 +39,41 @@
// Constants related to UI Command Codes.
// Refer to CEC Table 30 in HDMI Spec v1.4b.
+ static final int UI_COMMAND_POWER = 0x40;
static final int UI_COMMAND_MUTE = 0x43;
static final int UI_COMMAND_MUTE_FUNCTION = 0x65;
static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66;
+ static final int UI_COMMAND_POWER_ON_FUNCTION = 0x6D;
+
+ // Bit mask used to get the routing path of the top level device.
+ // When &'d with the path 1.2.2.0 (0x1220), for instance, gives 1.0.0.0.
+ static final int ROUTING_PATH_TOP_MASK = 0xF000;
+
+ // Flags used for setOption to CEC HAL.
+ /**
+ * When set to false, HAL does not wake up the system upon receiving
+ * <Image View On> or <Text View On>. Used when user changes the TV
+ * settings to disable the auto TV on functionality.
+ * True by default.
+ */
+ static final int FLAG_HDMI_OPTION_WAKEUP = 1;
+ /**
+ * When set to false, all the CEC commands are discarded. Used when
+ * user changes the TV settings to disable CEC functionality.
+ * True by default.
+ */
+ static final int FLAG_HDMI_OPTION_ENABLE_CEC = 2;
+ /**
+ * Setting this flag to false means Android system will stop handling
+ * CEC service and yield the control over to the microprocessor that is
+ * powered on through the standby mode. When set to true, the system
+ * will gain the control over, hence telling the microprocessor to stop
+ * handling the cec commands. This is called when system goes
+ * in and out of standby mode to notify the microprocessor that it should
+ * start/stop handling CEC commands on behalf of the system.
+ * False by default.
+ */
+ static final int FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL = 3;
private HdmiConstants() { /* cannot be instantiated */ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 0f3fc21..0d7ac8d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -21,27 +21,30 @@
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiHotplugEventListener;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;
import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
-import com.android.server.hdmi.HdmiCecLocalDevice.AddressAllocationCallback;
+import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
-import java.util.Locale;
/**
* Provides a service for sending and processing HDMI control messages,
@@ -57,6 +60,14 @@
static final int SEND_RESULT_NAK = -1;
static final int SEND_RESULT_FAILURE = -2;
+ static final int POLL_STRATEGY_MASK = 0x3; // first and second bit.
+ static final int POLL_STRATEGY_REMOTES_DEVICES = 0x1;
+ static final int POLL_STRATEGY_SYSTEM_AUDIO = 0x2;
+
+ static final int POLL_ITERATION_STRATEGY_MASK = 0x30000; // first and second bit.
+ static final int POLL_ITERATION_IN_ORDER = 0x10000;
+ static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
+
/**
* Interface to report send result.
*/
@@ -96,9 +107,8 @@
// Used to synchronize the access to the service.
private final Object mLock = new Object();
- // Type of logical devices hosted in the system.
- @GuardedBy("mLock")
- private final int[] mLocalDevices;
+ // Type of logical devices hosted in the system. Stored in the unmodifiable list.
+ private final List<Integer> mLocalDevices;
// List of listeners registered by callers that want to get notified of
// hotplug events.
@@ -108,49 +118,60 @@
private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
new ArrayList<>();
+ // Handler running on service thread. It's used to run a task in service thread.
+ private final Handler mHandler = new Handler();
+
+ private final HdmiCecMessageCache mCecMessageCache = new HdmiCecMessageCache();
+
@Nullable
private HdmiCecController mCecController;
@Nullable
private HdmiMhlController mMhlController;
+ // HDMI port information. Stored in the unmodifiable list to keep the static information
+ // from being modified.
+ private List<HdmiPortInfo> mPortInfo;
+
+ // Logical address of the active source.
+ @GuardedBy("mLock")
+ private int mActiveSource;
+
+ // Active routing path. Physical address of the active source but not all the time, such as
+ // when the new active source does not claim itself to be one.
+ @GuardedBy("mLock")
+ private int mActiveRoutingPath;
+
+ // Set to true while the service is in normal mode. While set to false, no input change is
+ // allowed. Used for situations where input change can confuse users such as channel auto-scan,
+ // system upgrade, etc., a.k.a. "prohibit mode".
+ @GuardedBy("mLock")
+ private boolean mInputChangeEnabled;
+
+ @GuardedBy("mLock")
// Whether ARC is "enabled" or not.
// TODO: it may need to hold lock if it's accessed from others.
private boolean mArcStatusEnabled = false;
+ @GuardedBy("mLock")
// Whether SystemAudioMode is "On" or not.
private boolean mSystemAudioMode;
- // Handler running on service thread. It's used to run a task in service thread.
- private final Handler mHandler = new Handler();
-
public HdmiControlService(Context context) {
super(context);
- mLocalDevices = getContext().getResources().getIntArray(
- com.android.internal.R.array.config_hdmiCecLogicalDeviceType);
+ mLocalDevices = HdmiUtils.asImmutableList(getContext().getResources().getIntArray(
+ com.android.internal.R.array.config_hdmiCecLogicalDeviceType));
+ // TODO: Get control flag from persistent storage
+ mInputChangeEnabled = true;
}
@Override
public void onStart() {
mIoThread.start();
mCecController = HdmiCecController.create(this);
+
if (mCecController != null) {
- mCecController.initializeLocalDevices(mLocalDevices, new AddressAllocationCallback() {
- private final SparseIntArray mAllocated = new SparseIntArray();
-
- @Override
- public void onAddressAllocated(int deviceType, int logicalAddress) {
- mAllocated.append(deviceType, logicalAddress);
- // TODO: get HdmiLCecLocalDevice and call onAddressAllocated here.
-
- // Once all logical allocation is done, launch device discovery
- // action if one of local device is TV.
- int tvAddress = mAllocated.get(HdmiCec.DEVICE_TV, -1);
- if (mLocalDevices.length == mAllocated.size() && tvAddress != -1) {
- launchDeviceDiscovery(tvAddress);
- }
- }
- });
+ initializeLocalDevices(mLocalDevices);
} else {
Slog.i(TAG, "Device does not support HDMI-CEC.");
}
@@ -159,13 +180,113 @@
if (mMhlController == null) {
Slog.i(TAG, "Device does not support MHL-control.");
}
-
+ mPortInfo = initPortInfo();
publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService());
// TODO: Read the preference for SystemAudioMode and initialize mSystemAudioMode and
// start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
}
+ private void initializeLocalDevices(final List<Integer> deviceTypes) {
+ // A container for [Logical Address, Local device info].
+ final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
+ final SparseIntArray finished = new SparseIntArray();
+ for (int type : deviceTypes) {
+ final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
+ localDevice.init();
+ mCecController.allocateLogicalAddress(type,
+ localDevice.getPreferredAddress(), new AllocateAddressCallback() {
+ @Override
+ public void onAllocated(int deviceType, int logicalAddress) {
+ if (logicalAddress == HdmiCec.ADDR_UNREGISTERED) {
+ Slog.e(TAG, "Failed to allocate address:[device_type:" + deviceType + "]");
+ } else {
+ HdmiCecDeviceInfo deviceInfo = createDeviceInfo(logicalAddress, deviceType);
+ localDevice.setDeviceInfo(deviceInfo);
+ mCecController.addLocalDevice(deviceType, localDevice);
+ mCecController.addLogicalAddress(logicalAddress);
+ devices.append(logicalAddress, localDevice);
+ }
+ finished.append(deviceType, logicalAddress);
+
+ // Once finish address allocation for all devices, notify
+ // it to each device.
+ if (deviceTypes.size() == finished.size()) {
+ notifyAddressAllocated(devices);
+ }
+ }
+ });
+ }
+ }
+
+ private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
+ for (int i = 0; i < devices.size(); ++i) {
+ int address = devices.keyAt(i);
+ HdmiCecLocalDevice device = devices.valueAt(i);
+ device.onAddressAllocated(address);
+ }
+ }
+
+ // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
+ // keep them in one place.
+ private List<HdmiPortInfo> initPortInfo() {
+ HdmiPortInfo[] cecPortInfo = null;
+
+ // CEC HAL provides majority of the info while MHL does only MHL support flag for
+ // each port. Return empty array if CEC HAL didn't provide the info.
+ if (mCecController != null) {
+ cecPortInfo = mCecController.getPortInfos();
+ }
+ if (cecPortInfo == null) {
+ return Collections.emptyList();
+ }
+
+ HdmiPortInfo[] mhlPortInfo = new HdmiPortInfo[0];
+ if (mMhlController != null) {
+ // TODO: Implement plumbing logic to get MHL port information.
+ // mhlPortInfo = mMhlController.getPortInfos();
+ }
+
+ // Use the id (port number) to find the matched info between CEC and MHL to combine them
+ // into one. Leave the field `mhlSupported` to false if matched MHL entry is not found.
+ ArrayList<HdmiPortInfo> result = new ArrayList<>(cecPortInfo.length);
+ for (int i = 0; i < cecPortInfo.length; ++i) {
+ HdmiPortInfo cec = cecPortInfo[i];
+ int id = cec.getId();
+ boolean mhlInfoFound = false;
+ for (HdmiPortInfo mhl : mhlPortInfo) {
+ if (id == mhl.getId()) {
+ result.add(new HdmiPortInfo(id, cec.getType(), cec.getAddress(),
+ cec.isCecSupported(), mhl.isMhlSupported(), cec.isArcSupported()));
+ mhlInfoFound = true;
+ break;
+ }
+ }
+ if (!mhlInfoFound) {
+ result.add(cec);
+ }
+ }
+
+ return Collections.unmodifiableList(result);
+ }
+
+ /**
+ * Returns HDMI port information for the given port id.
+ *
+ * @param portId HDMI port id
+ * @return {@link HdmiPortInfo} for the given port
+ */
+ HdmiPortInfo getPortInfo(int portId) {
+ // mPortInfo is an unmodifiable list and the only reference to its inner list.
+ // No lock is necessary.
+ for (HdmiPortInfo info : mPortInfo) {
+ if (portId == info.getId()) {
+ return info;
+ }
+ }
+ return null;
+ }
+
/**
* Returns {@link Looper} for IO operation.
*
@@ -185,6 +306,95 @@
return mHandler.getLooper();
}
+ int getActiveSource() {
+ synchronized (mLock) {
+ return mActiveSource;
+ }
+ }
+
+ int getActivePath() {
+ synchronized (mLock) {
+ return mActiveRoutingPath;
+ }
+ }
+
+ /**
+ * Returns the path (physical address) of the device at the top of the currently active
+ * routing path. Used to get the corresponding port address of the HDMI input of the TV.
+ */
+ int getActiveInput() {
+ synchronized (mLock) {
+ return mActiveRoutingPath & HdmiConstants.ROUTING_PATH_TOP_MASK;
+ }
+ }
+
+ void updateActiveDevice(int logicalAddress, int physicalAddress) {
+ synchronized (mLock) {
+ mActiveSource = logicalAddress;
+ mActiveRoutingPath = physicalAddress;
+ }
+ }
+
+ void setInputChangeEnabled(boolean enabled) {
+ synchronized (mLock) {
+ mInputChangeEnabled = enabled;
+ }
+ }
+
+ /**
+ * Returns physical address of the device.
+ */
+ int getPhysicalAddress() {
+ return mCecController.getPhysicalAddress();
+ }
+
+ /**
+ * Returns vendor id of CEC service.
+ */
+ int getVendorId() {
+ return mCecController.getVendorId();
+ }
+
+ HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
+ assertRunOnServiceThread();
+ return mCecController.getDeviceInfo(logicalAddress);
+ }
+
+ /**
+ * Returns version of CEC.
+ */
+ int getCecVersion() {
+ return mCecController.getVersion();
+ }
+
+ /**
+ * Returns a list of {@link HdmiCecDeviceInfo}.
+ *
+ * @param includeLocalDevice whether to include local devices
+ */
+ List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includeLocalDevice) {
+ assertRunOnServiceThread();
+ return mCecController.getDeviceInfoList(includeLocalDevice);
+ }
+
+ /**
+ * Returns the {@link HdmiCecDeviceInfo} instance whose physical address matches
+ * the given routing path. CEC devices use routing path for its physical address to
+ * describe the hierarchy of the devices in the network.
+ *
+ * @param path routing path or physical address
+ * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
+ */
+ HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
+ assertRunOnServiceThread();
+ for (HdmiCecDeviceInfo info : mCecController.getDeviceInfoList(false)) {
+ if (info.getPhysicalAddress() == path) {
+ return info;
+ }
+ }
+ return null;
+ }
+
/**
* Add and start a new {@link FeatureAction} to the action queue.
*
@@ -201,8 +411,20 @@
});
}
+ void setSystemAudioMode(boolean on) {
+ synchronized (mLock) {
+ mSystemAudioMode = on;
+ }
+ }
+
+ boolean getSystemAudioMode() {
+ synchronized (mLock) {
+ return mSystemAudioMode;
+ }
+ }
+
// See if we have an action of a given type in progress.
- private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
+ <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
for (FeatureAction action : mActions) {
if (action.getClass().equals(clazz)) {
return true;
@@ -222,7 +444,7 @@
}
// Remove all actions matched with the given Class type.
- private <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+ <T extends FeatureAction> void removeAction(final Class<T> clazz) {
removeActionExcept(clazz, null);
}
@@ -260,13 +482,18 @@
* @return {@code true} if ARC was in "Enabled" status
*/
boolean setArcStatus(boolean enabled) {
- boolean oldStatus = mArcStatusEnabled;
- // 1. Enable/disable ARC circuit.
- // TODO: call set_audio_return_channel of hal interface.
+ assertRunOnServiceThread();
+ synchronized (mLock) {
+ boolean oldStatus = mArcStatusEnabled;
+ // 1. Enable/disable ARC circuit.
+ mCecController.setAudioReturnChannel(enabled);
- // 2. Update arc status;
- mArcStatusEnabled = enabled;
- return oldStatus;
+ // TODO: notify arc mode change to AudioManager.
+
+ // 2. Update arc status;
+ mArcStatusEnabled = enabled;
+ return oldStatus;
+ }
}
/**
@@ -283,43 +510,20 @@
mCecController.sendCommand(command, null);
}
- /**
- * Add a new {@link HdmiCecDeviceInfo} to controller.
- *
- * @param deviceInfo new device information object to add
- */
- void addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
- // TODO: Implement this.
- }
-
boolean handleCecCommand(HdmiCecMessage message) {
+ // Cache incoming message. Note that it caches only white-listed one.
+ mCecMessageCache.cacheMessage(message);
+
// Commands that queries system information replies directly instead
// of creating FeatureAction because they are state-less.
+ // TODO: move the leftover message to local device.
switch (message.getOpcode()) {
- case HdmiCec.MESSAGE_GET_MENU_LANGUAGE:
- handleGetMenuLanguage(message);
- return true;
- case HdmiCec.MESSAGE_GIVE_OSD_NAME:
- handleGiveOsdName(message);
- return true;
- case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS:
- handleGivePhysicalAddress(message);
- return true;
- case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID:
- handleGiveDeviceVendorId(message);
- return true;
- case HdmiCec.MESSAGE_GET_CEC_VERSION:
- handleGetCecVersion(message);
- return true;
case HdmiCec.MESSAGE_INITIATE_ARC:
handleInitiateArc(message);
return true;
case HdmiCec.MESSAGE_TERMINATE_ARC:
handleTerminateArc(message);
return true;
- case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS:
- handleReportPhysicalAddress(message);
- return true;
case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE:
handleSetSystemAudioMode(message);
return true;
@@ -327,8 +531,22 @@
handleSystemAudioModeStatus(message);
return true;
default:
- return dispatchMessageToAction(message);
+ if (dispatchMessageToAction(message)) {
+ return true;
+ }
+ break;
}
+
+ return dispatchMessageToLocalDevice(message);
+ }
+
+ private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ if (device.dispatchMessage(message)) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -346,26 +564,62 @@
* devices.
*
* @param callback an interface used to get a list of all remote devices' address
+ * @param pickStrategy strategy how to pick polling candidates
* @param retryCount the number of retry used to send polling message to remote devices
+ * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
*/
- void pollDevices(DevicePollingCallback callback, int retryCount) {
- mCecController.pollDevices(callback, retryCount);
+ void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
+ mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount);
}
- private void handleReportPhysicalAddress(HdmiCecMessage message) {
- // At first, try to consume it.
- if (dispatchMessageToAction(message)) {
- return;
+ private int checkPollStrategy(int pickStrategy) {
+ int strategy = pickStrategy & POLL_STRATEGY_MASK;
+ if (strategy == 0) {
+ throw new IllegalArgumentException("Invalid poll strategy:" + pickStrategy);
}
-
- // Ignore if [Device Discovery Action] is on going ignore message.
- if (hasAction(DeviceDiscoveryAction.class)) {
- Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> "
- + "because Device Discovery Action is on-going:" + message);
- return;
+ int iterationStrategy = pickStrategy & POLL_ITERATION_STRATEGY_MASK;
+ if (iterationStrategy == 0) {
+ throw new IllegalArgumentException("Invalid iteration strategy:" + pickStrategy);
}
+ return strategy | iterationStrategy;
+ }
- // TODO: start new device action.
+ /**
+ * Launch device discovery sequence. It starts with clearing the existing device info list.
+ * Note that it assumes that logical address of all local devices is already allocated.
+ *
+ * @param sourceAddress a logical address of tv
+ */
+ void launchDeviceDiscovery(final int sourceAddress) {
+ // At first, clear all existing device infos.
+ mCecController.clearDeviceInfoList();
+ // TODO: flush cec message cache when CEC is turned off.
+
+ DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
+ new DeviceDiscoveryCallback() {
+ @Override
+ public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
+ for (HdmiCecDeviceInfo info : deviceInfos) {
+ addCecDevice(info);
+ }
+
+ // Add device info of all local devices.
+ for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
+ addCecDevice(device.getDeviceInfo());
+ }
+
+ addAndStartAction(new HotplugDetectionAction(HdmiControlService.this,
+ sourceAddress));
+ }
+ });
+ addAndStartAction(action);
+ }
+
+ private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
+ // TODO: find better name instead of model name.
+ String displayName = Build.MODEL;
+ return new HdmiCecDeviceInfo(logicalAddress,
+ getPhysicalAddress(), deviceType, getVendorId(), displayName);
}
private void handleInitiateArc(HdmiCecMessage message){
@@ -388,64 +642,6 @@
addAndStartAction(action);
}
- private void handleGetCecVersion(HdmiCecMessage message) {
- int version = mCecController.getVersion();
- HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
- message.getSource(),
- version);
- sendCecCommand(cecMessage);
- }
-
- private void handleGiveDeviceVendorId(HdmiCecMessage message) {
- int vendorId = mCecController.getVendorId();
- HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
- message.getDestination(), vendorId);
- sendCecCommand(cecMessage);
- }
-
- private void handleGivePhysicalAddress(HdmiCecMessage message) {
- int physicalAddress = mCecController.getPhysicalAddress();
- int deviceType = HdmiCec.getTypeFromAddress(message.getDestination());
- HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
- message.getDestination(), physicalAddress, deviceType);
- sendCecCommand(cecMessage);
- }
-
- private void handleGiveOsdName(HdmiCecMessage message) {
- // TODO: read device name from settings or property.
- String name = HdmiCec.getDefaultDeviceName(message.getDestination());
- HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
- message.getDestination(), message.getSource(), name);
- if (cecMessage != null) {
- sendCecCommand(cecMessage);
- } else {
- Slog.w(TAG, "Failed to build <Get Osd Name>:" + name);
- }
- }
-
- private void handleGetMenuLanguage(HdmiCecMessage message) {
- // Only 0 (TV), 14 (specific use) can answer.
- if (message.getDestination() != HdmiCec.ADDR_TV
- && message.getDestination() != HdmiCec.ADDR_SPECIFIC_USE) {
- Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
- sendCecCommand(
- HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(),
- message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE,
- HdmiConstants.ABORT_UNRECOGNIZED_MODE));
- return;
- }
-
- HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
- message.getDestination(),
- Locale.getDefault().getISO3Language());
- // TODO: figure out how to handle failed to get language code.
- if (command != null) {
- sendCecCommand(command);
- } else {
- Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString());
- }
- }
-
private boolean dispatchMessageToAction(HdmiCecMessage message) {
for (FeatureAction action : mActions) {
if (action.processCommand(message)) {
@@ -505,34 +701,6 @@
mCecController.addDeviceInfo(info);
}
- // Launch device discovery sequence.
- // It starts with clearing the existing device info list.
- // Note that it assumes that logical address of all local devices is already allocated.
- private void launchDeviceDiscovery(int sourceAddress) {
- // At first, clear all existing device infos.
- mCecController.clearDeviceInfoList();
-
- // TODO: check whether TV is one of local devices.
- DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress,
- new DeviceDiscoveryCallback() {
- @Override
- public void onDeviceDiscoveryDone(List<HdmiCecDeviceInfo> deviceInfos) {
- for (HdmiCecDeviceInfo info : deviceInfos) {
- mCecController.addDeviceInfo(info);
- }
-
- // Add device info of all local devices.
- for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
- mCecController.addDeviceInfo(device.getDeviceInfo());
- }
-
- // TODO: start hot-plug detection sequence here.
- // addAndStartAction(new HotplugDetectionAction());
- }
- });
- addAndStartAction(action);
- }
-
private void enforceAccessPermission() {
getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
}
@@ -541,12 +709,34 @@
@Override
public int[] getSupportedTypes() {
enforceAccessPermission();
- synchronized (mLock) {
- return mLocalDevices;
+ // mLocalDevices is an unmodifiable list - no lock necesary.
+ int[] localDevices = new int[mLocalDevices.size()];
+ for (int i = 0; i < localDevices.length; ++i) {
+ localDevices[i] = mLocalDevices.get(i);
}
+ return localDevices;
}
@Override
+ public void deviceSelect(final int logicalAddress, final IHdmiControlCallback callback) {
+ enforceAccessPermission();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ HdmiCecLocalDeviceTv tv =
+ (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiCec.DEVICE_TV);
+ if (tv == null) {
+ Slog.w(TAG, "Local playback device not available");
+ invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+ return;
+ }
+ tv.deviceSelect(logicalAddress, callback);
+ }
+ });
+ }
+
+
+ @Override
public void oneTouchPlay(final IHdmiControlCallback callback) {
enforceAccessPermission();
runOnServiceThread(new Runnable() {
@@ -677,27 +867,27 @@
return mCecController.getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
}
- void setSystemAudioMode(boolean newMode) {
- assertRunOnServiceThread();
- if (newMode != mSystemAudioMode) {
- // TODO: Need to set the preference for SystemAudioMode.
- // TODO: Need to handle the notification of changing the mode and
- // to identify the notification should be handled in the service or TvSettings.
- mSystemAudioMode = newMode;
- }
- }
-
- boolean getSystemAudioMode() {
- assertRunOnServiceThread();
- return mSystemAudioMode;
- }
-
void setAudioStatus(boolean mute, int volume) {
// TODO: Hook up with AudioManager.
}
boolean isInPresetInstallationMode() {
- // TODO: Implement this.
- return false;
+ synchronized (mLock) {
+ return !mInputChangeEnabled;
+ }
+ }
+
+ /**
+ * Called when a device is removed or removal of device is detected.
+ *
+ * @param address a logical address of a device to be removed
+ */
+ void removeCecDevice(int address) {
+ mCecController.removeDeviceInfo(address);
+ mCecMessageCache.flushMessagesFrom(address);
+ }
+
+ HdmiCecMessageCache getCecMessageCache() {
+ return mCecMessageCache;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index ef128ed1..b534377 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -20,6 +20,10 @@
import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* Various utilities to handle HDMI CEC messages.
*/
@@ -71,4 +75,40 @@
return cmd.getParams().length > 0
&& cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON;
}
+
+ /**
+ * Convert integer array to list of {@link Integer}.
+ *
+ * <p>The result is immutable.
+ *
+ * @param is integer array
+ * @return {@link List} instance containing the elements in the given array
+ */
+ static List<Integer> asImmutableList(final int[] is) {
+ ArrayList<Integer> list = new ArrayList<>(is.length);
+ for (int type : is) {
+ list.add(type);
+ }
+ return Collections.unmodifiableList(list);
+ }
+
+ /**
+ * Assemble two bytes into single integer value.
+ *
+ * @param data to be assembled
+ * @return assembled value
+ */
+ static int twoBytesToInt(byte[] data) {
+ return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
+ }
+
+ /**
+ * Assemble three bytes into single integer value.
+ *
+ * @param data to be assembled
+ * @return assembled value
+ */
+ static int threeBytesToInt(byte[] data) {
+ return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
new file mode 100644
index 0000000..c7a813d
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Feature action that handles hot-plug detection mechanism.
+ * Hot-plug event is initiated by timer after device discovery action.
+ *
+ * <p>Check all devices every 15 secs except for system audio.
+ * If system audio is on, check hot-plug for audio system every 5 secs.
+ * For other devices, keep 15 secs period.
+ */
+final class HotplugDetectionAction extends FeatureAction {
+ private static final String TAG = "HotPlugDetectionAction";
+
+ private static final int POLLING_INTERVAL_MS = 5000;
+ private static final int TIMEOUT_COUNT = 3;
+ private static final int POLL_RETRY_COUNT = 2;
+
+ // State in which waits for next polling
+ private static final int STATE_WAIT_FOR_NEXT_POLLING = 1;
+
+ // All addresses except for broadcast (unregistered address).
+ private static final int NUM_OF_ADDRESS = HdmiCec.ADDR_SPECIFIC_USE - HdmiCec.ADDR_TV + 1;
+
+ private int mTimeoutCount = 0;
+
+ /**
+ * Constructor
+ *
+ * @param service instance of {@link HdmiControlService}
+ * @param sourceAddress logical address of a device that initiate this action
+ */
+ HotplugDetectionAction(HdmiControlService service, int sourceAddress) {
+ super(service, sourceAddress);
+ }
+
+ @Override
+ boolean start() {
+ Slog.v(TAG, "Hot-plug dection started.");
+
+ mState = STATE_WAIT_FOR_NEXT_POLLING;
+ mTimeoutCount = 0;
+
+ // Start timer without polling.
+ // The first check for all devices will be initiated 15 seconds later.
+ addTimer(mState, POLLING_INTERVAL_MS);
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ // No-op
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ if (mState == STATE_WAIT_FOR_NEXT_POLLING) {
+ mTimeoutCount = (mTimeoutCount + 1) % TIMEOUT_COUNT;
+ pollDevices();
+ }
+ }
+
+ // This method is called every 5 seconds.
+ private void pollDevices() {
+ // All device check called every 15 seconds.
+ if (mTimeoutCount == 0) {
+ pollAllDevices();
+ } else {
+ if (mService.getSystemAudioMode()) {
+ pollAudioSystem();
+ }
+ }
+
+ addTimer(mState, POLLING_INTERVAL_MS);
+ }
+
+ private void pollAllDevices() {
+ Slog.v(TAG, "Poll all devices.");
+
+ mService.pollDevices(new DevicePollingCallback() {
+ @Override
+ public void onPollingFinished(List<Integer> ackedAddress) {
+ checkHotplug(ackedAddress, false);
+ }
+ }, HdmiControlService.POLL_ITERATION_IN_ORDER
+ | HdmiControlService.POLL_STRATEGY_REMOTES_DEVICES, POLL_RETRY_COUNT);
+ }
+
+ private void pollAudioSystem() {
+ Slog.v(TAG, "Poll audio system.");
+
+ mService.pollDevices(new DevicePollingCallback() {
+ @Override
+ public void onPollingFinished(List<Integer> ackedAddress) {
+ checkHotplug(ackedAddress, true);
+ }
+ }, HdmiControlService.POLL_ITERATION_IN_ORDER
+ | HdmiControlService.POLL_STRATEGY_SYSTEM_AUDIO, POLL_RETRY_COUNT);
+ }
+
+ private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
+ BitSet currentInfos = infoListToBitSet(mService.getDeviceInfoList(false), audioOnly);
+ BitSet polledResult = addressListToBitSet(ackedAddress);
+
+ // At first, check removed devices.
+ BitSet removed = complement(currentInfos, polledResult);
+ int index = -1;
+ while ((index = removed.nextSetBit(index + 1)) != -1) {
+ Slog.v(TAG, "Remove device by hot-plug detection:" + index);
+ removeDevice(index);
+ }
+
+ // Next, check added devices.
+ BitSet added = complement(polledResult, currentInfos);
+ index = -1;
+ while ((index = added.nextSetBit(index + 1)) != -1) {
+ Slog.v(TAG, "Add device by hot-plug detection:" + index);
+ addDevice(index);
+ }
+ }
+
+ private static BitSet infoListToBitSet(List<HdmiCecDeviceInfo> infoList, boolean audioOnly) {
+ BitSet set = new BitSet(NUM_OF_ADDRESS);
+ for (HdmiCecDeviceInfo info : infoList) {
+ if (audioOnly) {
+ if (info.getDeviceType() == HdmiCec.DEVICE_AUDIO_SYSTEM) {
+ set.set(info.getLogicalAddress());
+ }
+ } else {
+ set.set(info.getLogicalAddress());
+ }
+ }
+ return set;
+ }
+
+ private static BitSet addressListToBitSet(List<Integer> list) {
+ BitSet set = new BitSet(NUM_OF_ADDRESS);
+ for (Integer value : list) {
+ set.set(value);
+ }
+ return set;
+ }
+
+ // A - B = A & ~B
+ private static BitSet complement(BitSet first, BitSet second) {
+ // Need to clone it so that it doesn't touch original set.
+ BitSet clone = (BitSet) first.clone();
+ clone.andNot(second);
+ return clone;
+ }
+
+ private void addDevice(int addedAddress) {
+ // TODO: implement this.
+ }
+
+ private void removeDevice(int removedAddress) {
+ mService.removeCecDevice(removedAddress);
+ // TODO: implements following steps.
+ // 1. Launch routing control sequence
+ // 2. Stop one touch play sequence if removed device is the device to be selected.
+ // 3. If audio system, start system audio off and arc off
+ // 4. Inform device remove to others
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 156bbbe..c284d10 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -70,10 +70,13 @@
@Override
public boolean start() {
- sendCommand(
- HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress,
- mDeviceLogicalAddress));
mState = STATE_WAITING_FOR_SET_OSD_NAME;
+ if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_SET_OSD_NAME)) {
+ return true;
+ }
+
+ sendCommand(HdmiCecMessageBuilder.buildGiveOsdNameCommand(mSourceAddress,
+ mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
return true;
}
@@ -99,13 +102,11 @@
} catch (UnsupportedEncodingException e) {
Slog.e(TAG, "Failed to get OSD name: " + e.getMessage());
}
- mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
requestVendorId();
return true;
} else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) {
- int requestOpcode = params[1] & 0xff;
+ int requestOpcode = params[1] & 0xFF;
if (requestOpcode == HdmiCec.MESSAGE_SET_OSD_NAME) {
- mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
requestVendorId();
return true;
}
@@ -113,8 +114,7 @@
} else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) {
if (params.length == 3) {
- mVendorId = ((params[0] & 0xff) << 16) + ((params[1] & 0xff) << 8)
- + (params[2] & 0xff);
+ mVendorId = HdmiUtils.threeBytesToInt(params);
} else {
Slog.e(TAG, "Failed to get device vendor ID: ");
}
@@ -122,7 +122,7 @@
finish();
return true;
} else if (opcode == HdmiCec.MESSAGE_FEATURE_ABORT) {
- int requestOpcode = params[1] & 0xff;
+ int requestOpcode = params[1] & 0xFF;
if (requestOpcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) {
addDeviceInfo();
finish();
@@ -133,7 +133,21 @@
return false;
}
+ private boolean mayProcessCommandIfCached(int destAddress, int opcode) {
+ HdmiCecMessage message = mService.getCecMessageCache().getMessage(destAddress, opcode);
+ if (message != null) {
+ return processCommand(message);
+ }
+ return false;
+ }
+
private void requestVendorId() {
+ // At first, transit to waiting status for <Device Vendor Id>.
+ mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
+ // If the message is already in cache, process it.
+ if (mayProcessCommandIfCached(mDeviceLogicalAddress, HdmiCec.MESSAGE_DEVICE_VENDOR_ID)) {
+ return;
+ }
sendCommand(HdmiCecMessageBuilder.buildGiveDeviceVendorIdCommand(mSourceAddress,
mDeviceLogicalAddress));
addTimer(mState, TIMEOUT_MS);
@@ -143,7 +157,7 @@
if (mDisplayName == null) {
mDisplayName = HdmiCec.getDefaultDeviceName(mDeviceLogicalAddress);
}
- mService.addDeviceInfo(new HdmiCecDeviceInfo(
+ mService.addCecDevice(new HdmiCecDeviceInfo(
mDeviceLogicalAddress, mDevicePhysicalAddress,
HdmiCec.getTypeFromAddress(mDeviceLogicalAddress),
mVendorId, mDisplayName));
@@ -156,7 +170,6 @@
}
if (state == STATE_WAITING_FOR_SET_OSD_NAME) {
// Osd name request timed out. Try vendor id
- mState = STATE_WAITING_FOR_DEVICE_VENDOR_ID;
requestVendorId();
} else if (state == STATE_WAITING_FOR_DEVICE_VENDOR_ID) {
// vendor id timed out. Go ahead creating the device info what we've got so far.
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
new file mode 100644
index 0000000..26048d2
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import java.util.concurrent.TimeUnit;
+
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * Feature action for routing control. Exchanges routing-related commands with other devices
+ * to determine the new active source.
+ *
+ * <p>This action is initiated by various cases:
+ * <ul>
+ * <li> Manual TV input switching
+ * <li> Routing change of a CEC switch other than TV
+ * <li> New CEC device at the tail of the active routing path
+ * <li> Removed CEC device from the active routing path
+ * <li> Routing at CEC enable time
+ * </ul>
+ */
+public class RoutingControlAction extends FeatureAction {
+ private static final String TAG = "RoutingControlAction";
+
+ // State in which we wait for <Routing Information> to arrive. If timed out, we use the
+ // latest routing path to set the new active source.
+ private final static int STATE_WAIT_FOR_ROUTING_INFORMATION = 1;
+
+ // State in which we wait for <Report Power Status> in response to <Give Device Power Status>
+ // we have sent. If the response tells us the device power is on, we send <Set Stream Path>
+ // to make it the active source. Otherwise we do not send <Set Stream Path>, and possibly
+ // just show the blank screen.
+ private final static int STATE_WAIT_FOR_REPORT_POWER_STATUS = 2;
+
+ // Time out in millseconds used for <Routing Information>
+ private static final int TIMEOUT_ROUTING_INFORMATION_MS = 1000;
+
+ // Time out in milliseconds used for <Report Power Status>
+ private static final int TIMEOUT_REPORT_POWER_STATUS_MS = 1000;
+
+ private final IHdmiControlCallback mCallback;
+
+ // The latest routing path. Updated by each <Routing Information> from CEC switches.
+ private int mCurrentRoutingPath;
+
+ RoutingControlAction(HdmiControlService service, int sourceAddress, int portId,
+ IHdmiControlCallback callback) {
+ super(service, sourceAddress);
+ mCallback = callback;
+ mCurrentRoutingPath = portToPath(portId);
+ }
+
+ @Override
+ public boolean start() {
+ mState = STATE_WAIT_FOR_ROUTING_INFORMATION;
+ addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
+ return true;
+ }
+
+ @Override
+ public boolean processCommand(HdmiCecMessage cmd) {
+ int opcode = cmd.getOpcode();
+ byte[] params = cmd.getParams();
+ if (mState == STATE_WAIT_FOR_ROUTING_INFORMATION
+ && opcode == HdmiCec.MESSAGE_ROUTING_INFORMATION) {
+ // Keep updating the physicalAddress as we receive <Routing Information>.
+ // If the routing path doesn't belong to the currently active one, we should
+ // ignore it since it might have come from other, routing change sequence.
+ int routingPath = HdmiUtils.twoBytesToInt(params);
+ if (isInActiveRoutingPath(mCurrentRoutingPath, routingPath)) {
+ return true;
+ }
+ mCurrentRoutingPath = routingPath;
+ // Stop possible previous routing change sequence if in progress.
+ mService.removeAction(RoutingControlAction.class);
+ addTimer(mState, TIMEOUT_ROUTING_INFORMATION_MS);
+ return true;
+ } else if (mState == STATE_WAIT_FOR_REPORT_POWER_STATUS
+ && opcode == HdmiCec.MESSAGE_REPORT_POWER_STATUS) {
+ handleReportPowerStatus(cmd.getParams()[0]);
+ return true;
+ }
+ return false;
+ }
+
+ private void handleReportPowerStatus(int devicePowerStatus) {
+ int tvPowerStatus = getTvPowerStatus();
+ if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) {
+ if (isPowerStatusOnOrTransientToOn(devicePowerStatus)) {
+ sendSetStreamPath();
+ } else {
+ // The whole action should be stopped here if the device is in standby mode.
+ // We don't attempt to wake it up by sending <Set Stream Path>.
+ }
+ invokeCallback(HdmiCec.RESULT_SUCCESS);
+ finish();
+ } else {
+ // TV is going into standby mode.
+ // TODO: Figure out what to do.
+ }
+ }
+
+ private int getTvPowerStatus() {
+ // TODO: Obtain TV power status.
+ return HdmiCec.POWER_STATUS_ON;
+ }
+
+ private static boolean isPowerStatusOnOrTransientToOn(int status) {
+ return status == HdmiCec.POWER_STATUS_ON || status == HdmiCec.POWER_STATUS_TRANSIENT_TO_ON;
+ }
+
+ private void sendSetStreamPath() {
+ sendCommand(HdmiCecMessageBuilder.buildSetStreamPath(mSourceAddress, mCurrentRoutingPath));
+ }
+
+ private static boolean isInActiveRoutingPath(int activePath, int newPath) {
+ // Check each nibble of the currently active path and the new path till the position
+ // where the active nibble is not zero. For (activePath, newPath),
+ // (1.1.0.0, 1.0.0.0) -> true, new path is a parent
+ // (1.2.1.0, 1.2.1.2) -> true, new path is a descendant
+ // (1.1.0.0, 1.2.0.0) -> false, new path is a sibling
+ // (1.0.0.0, 2.0.0.0) -> false, in a completely different path
+ for (int i = 12; i >= 0; i -= 4) {
+ int nibbleActive = (activePath >> i) & 0xF;
+ if (nibbleActive == 0) {
+ break;
+ }
+ int nibbleNew = (newPath >> i) & 0xF;
+ if (nibbleNew == 0) {
+ break;
+ }
+ if (nibbleActive != nibbleNew) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void handleTimerEvent(int timeoutState) {
+ if (mState != timeoutState || mState == STATE_NONE) {
+ Slog.w("CEC", "Timer in a wrong state. Ignored.");
+ return;
+ }
+ switch (timeoutState) {
+ case STATE_WAIT_FOR_ROUTING_INFORMATION:
+ HdmiCecDeviceInfo device = mService.getDeviceInfoByPath(mCurrentRoutingPath);
+ if (device == null) {
+ maybeChangeActiveInput(pathToPort(mCurrentRoutingPath));
+ } else {
+ // TODO: Also check followings and then proceed:
+ // if routing change was neither triggered by TV at CEC enable time, nor
+ // at the detection of new device at the end of the active routing path, nor
+ // by TV power on with HDMI input as the active signal source.
+ int deviceLogicalAddress = device.getLogicalAddress();
+ queryDevicePowerStatus(deviceLogicalAddress, new SendMessageCallback() {
+ @Override
+ public void onSendCompleted(int error) {
+ handlDevicePowerStatusAckResult(error == HdmiCec.RESULT_SUCCESS);
+ }
+ });
+ }
+ return;
+ case STATE_WAIT_FOR_REPORT_POWER_STATUS:
+ int tvPowerStatus = getTvPowerStatus();
+ if (isPowerStatusOnOrTransientToOn(tvPowerStatus)) {
+ if (!maybeChangeActiveInput(pathToPort(mCurrentRoutingPath))) {
+ sendSetStreamPath();
+ }
+ }
+ invokeCallback(HdmiCec.RESULT_SUCCESS);
+ finish();
+ return;
+ }
+ }
+
+ // Called whenever an HDMI input of the TV shall become the active input.
+ private boolean maybeChangeActiveInput(int inputPortPath) {
+ if (mService.getActiveInput() == inputPortPath) {
+ return false;
+ }
+ // TODO: Remember the currently active input
+ // if PAP/PIP is active, move the focus to the right window, otherwise switch
+ // the port.
+ // Show the OSD input change banner.
+ return true;
+ }
+
+ private void queryDevicePowerStatus(int address, SendMessageCallback callback) {
+ sendCommand(HdmiCecMessageBuilder.buildGiveDevicePowerStatus(mSourceAddress, address),
+ callback);
+ }
+
+ private void handlDevicePowerStatusAckResult(boolean acked) {
+ if (acked) {
+ mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
+ addTimer(mState, TIMEOUT_REPORT_POWER_STATUS_MS);
+ } else {
+ maybeChangeActiveInput(pathToPort(mCurrentRoutingPath));
+ }
+ }
+
+ // Get the address of the TV port to which the given path is connected.
+ private static int pathToPort(int path) {
+ return path & HdmiConstants.ROUTING_PATH_TOP_MASK;
+ }
+
+ // Given the HDMI port id, return the port address.
+ private int portToPath(int portId) {
+ return mService.getPortInfo(portId).getAddress();
+ }
+
+ private void invokeCallback(int result) {
+ if (mCallback == null) {
+ return;
+ }
+ try {
+ mCallback.onComplete(result);
+ } catch (RemoteException e) {
+ // Do nothing.
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
new file mode 100644
index 0000000..5eb9315
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCecMessage;
+import android.view.KeyEvent;
+import android.util.Slog;
+
+import libcore.util.EmptyArray;
+
+/**
+ * Feature action that transmits remote control key command (User Control Press/
+ * User Control Release) to CEC bus.
+ *
+ * <p>This action is created when a new key event is passed to CEC service. It optionally
+ * does key repeat (a.k.a. press-and-hold) operation until it receives a key release event.
+ * If another key press event is received before the key in use is released, CEC service
+ * does not create a new action but recycles the current one by updating the key used
+ * for press-and-hold operation.
+ *
+ * <p>Package-private, accessed by {@link HdmiControlService} only.
+ */
+final class SendKeyAction extends FeatureAction {
+ private static final String TAG = "SendKeyAction";
+
+ // State in which the action is at work. The state is set in {@link #start()} and
+ // persists throughout the process till it is set back to {@code STATE_NONE} at the end.
+ private static final int STATE_PROCESSING_KEYCODE = 1;
+
+ // IRT(Initiator Repetition Time) in millisecond as recommended in the standard.
+ // Outgoing UCP commands, when in 'Press and Hold' mode, should be this much apart
+ // from the adjacent one so as not to place unnecessarily heavy load on the CEC line.
+ // TODO: This value might need tweaking per product basis. Consider putting it
+ // in config.xml to allow customization.
+ private static final int IRT_MS = 450;
+
+ // Logical address of the device to which the UCP/UCP commands are sent.
+ private final int mTargetAddress;
+
+ // The key code of the last key press event the action is passed via processKeyEvent.
+ private int mLastKeyCode;
+
+ /**
+ * Constructor.
+ *
+ * @param service {@link HdmiControlService} instance
+ * @param sourceAddress logical address to be used as source address
+ * @param targetAddress logical address of the device to send the keys to
+ * @param keyCode remote control key code as defined in {@link KeyEvent}
+ */
+ SendKeyAction(HdmiControlService service, int sourceAddress, int targetAddress, int keyCode) {
+ super(service, sourceAddress);
+ mTargetAddress = targetAddress;
+ mLastKeyCode = keyCode;
+ }
+
+ @Override
+ public boolean start() {
+ sendKeyDown(mLastKeyCode);
+ mState = STATE_PROCESSING_KEYCODE;
+ addTimer(mState, IRT_MS);
+ return true;
+ }
+
+ /**
+ * Called when a key event should be handled for the action.
+ *
+ * @param keyCode key code of {@link KeyEvent} object
+ * @param isPressed true if the key event is of {@link KeyEvent#ACTION_DOWN}
+ * @param param additional parameter that comes with the key event
+ */
+ void processKeyEvent(int keyCode, boolean isPressed) {
+ if (mState != STATE_PROCESSING_KEYCODE) {
+ Slog.w(TAG, "Not in a valid state");
+ return;
+ }
+ // A new key press event that comes in with a key code different from the last
+ // one sets becomes a new key code to be used for press-and-hold operation.
+ // Removes any pending timer and starts a new timer for itself.
+ // Key release event indicates that the action shall be finished. Send UCR
+ // command and terminate the action. Other release events are ignored.
+ if (isPressed) {
+ if (keyCode != mLastKeyCode) {
+ mActionTimer.clearTimerMessage();
+ sendKeyDown(keyCode);
+ addTimer(mState, IRT_MS);
+ mLastKeyCode = keyCode;
+ }
+ } else {
+ if (keyCode == mLastKeyCode) {
+ sendKeyUp();
+ finish();
+ }
+ }
+ }
+
+ private void sendKeyDown(int keyCode) {
+ byte[] keyCodeAndParam = getCecKeyCodeAndParam(keyCode);
+ if (keyCodeAndParam == null) {
+ return;
+ }
+ sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(mSourceAddress, mTargetAddress,
+ keyCodeAndParam));
+ }
+
+ private void sendKeyUp() {
+ sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(mSourceAddress, mTargetAddress));
+ }
+
+ @Override
+ public boolean processCommand(HdmiCecMessage cmd) {
+ // Send key action doesn't need any incoming CEC command, hence does not consume it.
+ return false;
+ }
+
+ @Override
+ public void handleTimerEvent(int state) {
+ // Timer event occurs every IRT_MS milliseconds to perform key-repeat (or press-and-hold)
+ // operation. If the last received key code is as same as the one with which the action
+ // is started, plus there was no key release event in last IRT_MS timeframe, send a UCP
+ // command and start another timer to schedule the next press-and-hold command.
+ if (mState != STATE_PROCESSING_KEYCODE) {
+ Slog.w(TAG, "Not in a valid state");
+ return;
+ }
+ sendKeyDown(mLastKeyCode);
+ addTimer(mState, IRT_MS);
+ }
+
+ // Converts the Android key code to corresponding CEC key code definition. Those CEC keys
+ // with additional parameters should be mapped from individual Android key code. 'Select
+ // Broadcast' with the parameter 'cable', for instance, shall have its counterpart such as
+ // KeyEvent.KEYCODE_TV_BROADCAST_CABLE.
+ // The return byte array contains both UI command (keycode) and optional parameter.
+ private byte[] getCecKeyCodeAndParam(int keyCode) {
+ // TODO: Convert to CEC keycode and (optionally) parameter.
+ // return androidKeyToCecKey(keyCode);
+ return EmptyArray.BYTE;
+ }
+}
diff --git a/services/core/java/com/android/server/job/JobCompletedListener.java b/services/core/java/com/android/server/job/JobCompletedListener.java
new file mode 100644
index 0000000..a7af9cd
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobCompletedListener.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job;
+
+import com.android.server.job.controllers.JobStatus;
+
+/**
+ * Used for communication between {@link com.android.server.job.JobServiceContext} and the
+ * {@link com.android.server.job.JobSchedulerService}.
+ */
+public interface JobCompletedListener {
+
+ /**
+ * Callback for when a job is completed.
+ * @param needsReschedule Whether the implementing class should reschedule this job.
+ */
+ public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule);
+}
diff --git a/services/core/java/com/android/server/job/JobMapReadFinishedListener.java b/services/core/java/com/android/server/job/JobMapReadFinishedListener.java
new file mode 100644
index 0000000..f3e77e6
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobMapReadFinishedListener.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job;
+
+import java.util.List;
+
+import com.android.server.job.controllers.JobStatus;
+
+/**
+ * Callback definition for I/O thread to let the JobManagerService know when
+ * I/O read has completed. Done this way so we don't stall the main thread on
+ * boot.
+ */
+public interface JobMapReadFinishedListener {
+
+ /**
+ * Called by the {@link JobStore} at boot, when the disk read is finished.
+ */
+ public void onJobMapReadFinished(List<JobStatus> jobs);
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
new file mode 100644
index 0000000..0e9a9cc
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.app.job.IJobScheduler;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.server.job.controllers.BatteryController;
+import com.android.server.job.controllers.ConnectivityController;
+import com.android.server.job.controllers.IdleController;
+import com.android.server.job.controllers.JobStatus;
+import com.android.server.job.controllers.StateController;
+import com.android.server.job.controllers.TimeController;
+
+import java.util.LinkedList;
+
+/**
+ * Responsible for taking jobs representing work to be performed by a client app, and determining
+ * based on the criteria specified when that job should be run against the client application's
+ * endpoint.
+ * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
+ * about constraints, or the state of active jobs. It receives callbacks from the various
+ * controllers and completed jobs and operates accordingly.
+ *
+ * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
+ * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
+ * @hide
+ */
+public class JobSchedulerService extends com.android.server.SystemService
+ implements StateChangedListener, JobCompletedListener, JobMapReadFinishedListener {
+ // TODO: Switch this off for final version.
+ static final boolean DEBUG = true;
+ /** The number of concurrent jobs we run at one time. */
+ private static final int MAX_JOB_CONTEXTS_COUNT = 3;
+ static final String TAG = "JobManagerService";
+ /** Master list of jobs. */
+ private final JobStore mJobs;
+
+ static final int MSG_JOB_EXPIRED = 0;
+ static final int MSG_CHECK_JOB = 1;
+
+ // Policy constants
+ /**
+ * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
+ * early.
+ */
+ private static final int MIN_IDLE_COUNT = 1;
+ /**
+ * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
+ * things early.
+ */
+ private static final int MIN_CONNECTIVITY_COUNT = 2;
+ /**
+ * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running
+ * some work early.
+ */
+ private static final int MIN_READY_JOBS_COUNT = 4;
+
+ /**
+ * Track Services that have currently active or pending jobs. The index is provided by
+ * {@link JobStatus#getServiceToken()}
+ */
+ private final List<JobServiceContext> mActiveServices = new LinkedList<JobServiceContext>();
+ /** List of controllers that will notify this service of updates to jobs. */
+ private List<StateController> mControllers;
+ /**
+ * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
+ * when ready to execute them.
+ */
+ private final LinkedList<JobStatus> mPendingJobs = new LinkedList<JobStatus>();
+
+ private final JobHandler mHandler;
+ private final JobSchedulerStub mJobSchedulerStub;
+ /**
+ * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
+ * still clean up. On reinstall the package will have a new uid.
+ */
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Slog.d(TAG, "Receieved: " + intent.getAction());
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (DEBUG) {
+ Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
+ }
+ cancelJobsForUid(uidRemoved);
+ } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ if (DEBUG) {
+ Slog.d(TAG, "Removing jobs for user: " + userId);
+ }
+ cancelJobsForUser(userId);
+ }
+ }
+ };
+
+ /**
+ * Entry point from client to schedule the provided job.
+ * This cancels the job if it's already been scheduled, and replaces it with the one provided.
+ * @param job JobInfo object containing execution parameters
+ * @param uId The package identifier of the application this job is for.
+ * @param canPersistJob Whether or not the client has the appropriate permissions for
+ * persisting this job.
+ * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
+ */
+ public int schedule(JobInfo job, int uId, boolean canPersistJob) {
+ JobStatus jobStatus = new JobStatus(job, uId, canPersistJob);
+ cancelJob(uId, job.getId());
+ startTrackingJob(jobStatus);
+ return JobScheduler.RESULT_SUCCESS;
+ }
+
+ public List<JobInfo> getPendingJobs(int uid) {
+ ArrayList<JobInfo> outList = new ArrayList<JobInfo>();
+ synchronized (mJobs) {
+ for (JobStatus job : mJobs.getJobs()) {
+ if (job.getUid() == uid) {
+ outList.add(job.getJob());
+ }
+ }
+ }
+ return outList;
+ }
+
+ private void cancelJobsForUser(int userHandle) {
+ synchronized (mJobs) {
+ List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
+ for (JobStatus toRemove : jobsForUser) {
+ if (DEBUG) {
+ Slog.d(TAG, "Cancelling: " + toRemove);
+ }
+ cancelJobLocked(toRemove);
+ }
+ }
+ }
+
+ /**
+ * Entry point from client to cancel all jobs originating from their uid.
+ * This will remove the job from the master list, and cancel the job if it was staged for
+ * execution or being executed.
+ * @param uid To check against for removal of a job.
+ */
+ public void cancelJobsForUid(int uid) {
+ // Remove from master list.
+ synchronized (mJobs) {
+ List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
+ for (JobStatus toRemove : jobsForUid) {
+ if (DEBUG) {
+ Slog.d(TAG, "Cancelling: " + toRemove);
+ }
+ cancelJobLocked(toRemove);
+ }
+ }
+ }
+
+ /**
+ * Entry point from client to cancel the job corresponding to the jobId provided.
+ * This will remove the job from the master list, and cancel the job if it was staged for
+ * execution or being executed.
+ * @param uid Uid of the calling client.
+ * @param jobId Id of the job, provided at schedule-time.
+ */
+ public void cancelJob(int uid, int jobId) {
+ JobStatus toCancel;
+ synchronized (mJobs) {
+ toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
+ if (toCancel != null) {
+ cancelJobLocked(toCancel);
+ }
+ }
+ }
+
+ private void cancelJobLocked(JobStatus cancelled) {
+ // Remove from store.
+ stopTrackingJob(cancelled);
+ // Remove from pending queue.
+ mPendingJobs.remove(cancelled);
+ // Cancel if running.
+ stopJobOnServiceContextLocked(cancelled);
+ }
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public JobSchedulerService(Context context) {
+ super(context);
+ // Create the controllers.
+ mControllers = new LinkedList<StateController>();
+ mControllers.add(ConnectivityController.get(this));
+ mControllers.add(TimeController.get(this));
+ mControllers.add(IdleController.get(this));
+ mControllers.add(BatteryController.get(this));
+
+ mHandler = new JobHandler(context.getMainLooper());
+ mJobSchedulerStub = new JobSchedulerStub();
+ // Create the "runners".
+ for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
+ mActiveServices.add(
+ new JobServiceContext(this, context.getMainLooper()));
+ }
+ mJobs = JobStore.initAndGet(this);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (PHASE_SYSTEM_SERVICES_READY == phase) {
+ // Register br for package removals and user removals.
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ getContext().registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+ final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+ getContext().registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+ }
+ }
+
+ /**
+ * Called when we have a job status object that we need to insert in our
+ * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
+ * about.
+ */
+ private void startTrackingJob(JobStatus jobStatus) {
+ boolean update;
+ synchronized (mJobs) {
+ update = mJobs.add(jobStatus);
+ }
+ for (StateController controller : mControllers) {
+ if (update) {
+ controller.maybeStopTrackingJob(jobStatus);
+ }
+ controller.maybeStartTrackingJob(jobStatus);
+ }
+ }
+
+ /**
+ * Called when we want to remove a JobStatus object that we've finished executing. Returns the
+ * object removed.
+ */
+ private boolean stopTrackingJob(JobStatus jobStatus) {
+ boolean removed;
+ synchronized (mJobs) {
+ // Remove from store as well as controllers.
+ removed = mJobs.remove(jobStatus);
+ }
+ if (removed) {
+ for (StateController controller : mControllers) {
+ controller.maybeStopTrackingJob(jobStatus);
+ }
+ }
+ return removed;
+ }
+
+ private boolean stopJobOnServiceContextLocked(JobStatus job) {
+ for (JobServiceContext jsc : mActiveServices) {
+ final JobStatus executing = jsc.getRunningJob();
+ if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
+ jsc.cancelExecutingJob();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @param job JobStatus we are querying against.
+ * @return Whether or not the job represented by the status object is currently being run or
+ * is pending.
+ */
+ private boolean isCurrentlyActiveLocked(JobStatus job) {
+ for (JobServiceContext serviceContext : mActiveServices) {
+ final JobStatus running = serviceContext.getRunningJob();
+ if (running != null && running.matches(job.getUid(), job.getJobId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * A job is rescheduled with exponential back-off if the client requests this from their
+ * execution logic.
+ * A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the
+ * timeliness of the reschedule. For an idle-mode job, no deadline is given.
+ * @param failureToReschedule Provided job status that we will reschedule.
+ * @return A newly instantiated JobStatus with the same constraints as the last job except
+ * with adjusted timing constraints.
+ */
+ private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
+ final long elapsedNowMillis = SystemClock.elapsedRealtime();
+ final JobInfo job = failureToReschedule.getJob();
+
+ final long initialBackoffMillis = job.getInitialBackoffMillis();
+ final int backoffAttempt = failureToReschedule.getNumFailures() + 1;
+ long newEarliestRuntimeElapsed = elapsedNowMillis;
+
+ switch (job.getBackoffPolicy()) {
+ case JobInfo.BackoffPolicy.LINEAR:
+ newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt;
+ break;
+ default:
+ if (DEBUG) {
+ Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
+ }
+ case JobInfo.BackoffPolicy.EXPONENTIAL:
+ newEarliestRuntimeElapsed +=
+ Math.pow(initialBackoffMillis * 0.001, backoffAttempt) * 1000;
+ break;
+ }
+ newEarliestRuntimeElapsed =
+ Math.min(newEarliestRuntimeElapsed, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
+ return new JobStatus(failureToReschedule, newEarliestRuntimeElapsed,
+ JobStatus.NO_LATEST_RUNTIME, backoffAttempt);
+ }
+
+ /**
+ * Called after a periodic has executed so we can to re-add it. We take the last execution time
+ * of the job to be the time of completion (i.e. the time at which this function is called).
+ * This could be inaccurate b/c the job can run for as long as
+ * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
+ * to underscheduling at least, rather than if we had taken the last execution time to be the
+ * start of the execution.
+ * @return A new job representing the execution criteria for this instantiation of the
+ * recurring job.
+ */
+ private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
+ final long elapsedNow = SystemClock.elapsedRealtime();
+ // Compute how much of the period is remaining.
+ long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
+ long newEarliestRunTimeElapsed = elapsedNow + runEarly;
+ long period = periodicToReschedule.getJob().getIntervalMillis();
+ long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
+
+ if (DEBUG) {
+ Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
+ newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
+ }
+ return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
+ newLatestRuntimeElapsed, 0 /* backoffAttempt */);
+ }
+
+ // JobCompletedListener implementations.
+
+ /**
+ * A job just finished executing. We fetch the
+ * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
+ * whether we want to reschedule we readd it to the controllers.
+ * @param jobStatus Completed job.
+ * @param needsReschedule Whether the implementing class should reschedule this job.
+ */
+ @Override
+ public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
+ if (DEBUG) {
+ Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
+ }
+ if (!stopTrackingJob(jobStatus)) {
+ if (DEBUG) {
+ Slog.e(TAG, "Error removing job: could not find job to remove. Was job " +
+ "removed while executing?");
+ }
+ return;
+ }
+ if (needsReschedule) {
+ JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
+ startTrackingJob(rescheduled);
+ } else if (jobStatus.getJob().isPeriodic()) {
+ JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
+ startTrackingJob(rescheduledPeriodic);
+ }
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ }
+
+ // StateChangedListener implementations.
+
+ /**
+ * Off-board work to our handler thread as quickly as possible, b/c this call is probably being
+ * made on the main thread.
+ * For now this takes the job and if it's ready to run it will run it. In future we might not
+ * provide the job, so that the StateChangedListener has to run through its list of jobs to
+ * see which are ready. This will further decouple the controllers from the execution logic.
+ */
+ @Override
+ public void onControllerStateChanged() {
+ // Post a message to to run through the list of jobs and start/stop any that are eligible.
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+ }
+
+ @Override
+ public void onRunJobNow(JobStatus jobStatus) {
+ mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
+ }
+
+ /**
+ * Disk I/O is finished, take the list of jobs we read from disk and add them to our
+ * {@link JobStore}.
+ * This is run on the {@link com.android.server.IoThread} instance, which is a separate thread,
+ * and is called once at boot.
+ */
+ @Override
+ public void onJobMapReadFinished(List<JobStatus> jobs) {
+ synchronized (mJobs) {
+ for (JobStatus js : jobs) {
+ if (mJobs.containsJobIdForUid(js.getJobId(), js.getUid())) {
+ // An app with BOOT_COMPLETED *might* have decided to reschedule their job, in
+ // the same amount of time it took us to read it from disk. If this is the case
+ // we leave it be.
+ continue;
+ }
+ startTrackingJob(js);
+ }
+ }
+ }
+
+ private class JobHandler extends Handler {
+
+ public JobHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_JOB_EXPIRED:
+ synchronized (mJobs) {
+ JobStatus runNow = (JobStatus) message.obj;
+ if (!mPendingJobs.contains(runNow)) {
+ mPendingJobs.add(runNow);
+ }
+ }
+ queueReadyJobsForExecutionH();
+ break;
+ case MSG_CHECK_JOB:
+ // Check the list of jobs and run some of them if we feel inclined.
+ maybeQueueReadyJobsForExecutionH();
+ break;
+ }
+ maybeRunPendingJobsH();
+ // Don't remove JOB_EXPIRED in case one came along while processing the queue.
+ removeMessages(MSG_CHECK_JOB);
+ }
+
+ /**
+ * Run through list of jobs and execute all possible - at least one is expired so we do
+ * as many as we can.
+ */
+ private void queueReadyJobsForExecutionH() {
+ synchronized (mJobs) {
+ for (JobStatus job : mJobs.getJobs()) {
+ if (isReadyToBeExecutedLocked(job)) {
+ mPendingJobs.add(job);
+ } else if (isReadyToBeCancelledLocked(job)) {
+ stopJobOnServiceContextLocked(job);
+ }
+ }
+ }
+ }
+
+ /**
+ * The state of at least one job has changed. Here is where we could enforce various
+ * policies on when we want to execute jobs.
+ * Right now the policy is such:
+ * If >1 of the ready jobs is idle mode we send all of them off
+ * if more than 2 network connectivity jobs are ready we send them all off.
+ * If more than 4 jobs total are ready we send them all off.
+ * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
+ */
+ private void maybeQueueReadyJobsForExecutionH() {
+ synchronized (mJobs) {
+ int idleCount = 0;
+ int backoffCount = 0;
+ int connectivityCount = 0;
+ List<JobStatus> runnableJobs = new ArrayList<JobStatus>();
+ for (JobStatus job : mJobs.getJobs()) {
+ if (isReadyToBeExecutedLocked(job)) {
+ if (job.getNumFailures() > 0) {
+ backoffCount++;
+ }
+ if (job.hasIdleConstraint()) {
+ idleCount++;
+ }
+ if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()) {
+ connectivityCount++;
+ }
+ runnableJobs.add(job);
+ } else if (isReadyToBeCancelledLocked(job)) {
+ stopJobOnServiceContextLocked(job);
+ }
+ }
+ if (backoffCount > 0 || idleCount >= MIN_IDLE_COUNT ||
+ connectivityCount >= MIN_CONNECTIVITY_COUNT ||
+ runnableJobs.size() >= MIN_READY_JOBS_COUNT) {
+ for (JobStatus job : runnableJobs) {
+ mPendingJobs.add(job);
+ }
+ }
+ }
+ }
+
+ /**
+ * Criteria for moving a job into the pending queue:
+ * - It's ready.
+ * - It's not pending.
+ * - It's not already running on a JSC.
+ */
+ private boolean isReadyToBeExecutedLocked(JobStatus job) {
+ return job.isReady() && !mPendingJobs.contains(job) && !isCurrentlyActiveLocked(job);
+ }
+
+ /**
+ * Criteria for cancelling an active job:
+ * - It's not ready
+ * - It's running on a JSC.
+ */
+ private boolean isReadyToBeCancelledLocked(JobStatus job) {
+ return !job.isReady() && isCurrentlyActiveLocked(job);
+ }
+
+ /**
+ * Reconcile jobs in the pending queue against available execution contexts.
+ * A controller can force a job into the pending queue even if it's already running, but
+ * here is where we decide whether to actually execute it.
+ */
+ private void maybeRunPendingJobsH() {
+ synchronized (mJobs) {
+ Iterator<JobStatus> it = mPendingJobs.iterator();
+ while (it.hasNext()) {
+ JobStatus nextPending = it.next();
+ JobServiceContext availableContext = null;
+ for (JobServiceContext jsc : mActiveServices) {
+ final JobStatus running = jsc.getRunningJob();
+ if (running != null && running.matches(nextPending.getUid(),
+ nextPending.getJobId())) {
+ // Already running this tId for this uId, skip.
+ availableContext = null;
+ break;
+ }
+ if (jsc.isAvailable()) {
+ availableContext = jsc;
+ }
+ }
+ if (availableContext != null) {
+ if (!availableContext.executeRunnableJob(nextPending)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Error executing " + nextPending);
+ }
+ mJobs.remove(nextPending);
+ }
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Binder stub trampoline implementation
+ */
+ final class JobSchedulerStub extends IJobScheduler.Stub {
+ /** Cache determination of whether a given app can persist jobs
+ * key is uid of the calling app; value is undetermined/true/false
+ */
+ private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
+
+ // Enforce that only the app itself (or shared uid participant) can schedule a
+ // job that runs one of the app's services, as well as verifying that the
+ // named service properly requires the BIND_JOB_SERVICE permission
+ private void enforceValidJobRequest(int uid, JobInfo job) {
+ final PackageManager pm = getContext().getPackageManager();
+ final ComponentName service = job.getService();
+ try {
+ ServiceInfo si = pm.getServiceInfo(service, 0);
+ if (si.applicationInfo.uid != uid) {
+ throw new IllegalArgumentException("uid " + uid +
+ " cannot schedule job in " + service.getPackageName());
+ }
+ if (!JobService.PERMISSION_BIND.equals(si.permission)) {
+ throw new IllegalArgumentException("Scheduled service " + service
+ + " does not require android.permission.BIND_JOB_SERVICE permission");
+ }
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException("No such service: " + service);
+ }
+ }
+
+ private boolean canPersistJobs(int pid, int uid) {
+ // If we get this far we're good to go; all we need to do now is check
+ // whether the app is allowed to persist its scheduled work.
+ final boolean canPersist;
+ synchronized (mPersistCache) {
+ Boolean cached = mPersistCache.get(uid);
+ if (cached != null) {
+ canPersist = cached.booleanValue();
+ } else {
+ // Persisting jobs is tantamount to running at boot, so we permit
+ // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
+ // permission
+ int result = getContext().checkPermission(
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
+ canPersist = (result == PackageManager.PERMISSION_GRANTED);
+ mPersistCache.put(uid, canPersist);
+ }
+ }
+ return canPersist;
+ }
+
+ // IJobScheduler implementation
+ @Override
+ public int schedule(JobInfo job) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling job: " + job);
+ }
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+
+ enforceValidJobRequest(uid, job);
+ final boolean canPersist = canPersistJobs(pid, uid);
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return JobSchedulerService.this.schedule(job, uid, canPersist);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public List<JobInfo> getAllPendingJobs() throws RemoteException {
+ final int uid = Binder.getCallingUid();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return JobSchedulerService.this.getPendingJobs(uid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void cancelAll() throws RemoteException {
+ final int uid = Binder.getCallingUid();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ JobSchedulerService.this.cancelJobsForUid(uid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public void cancel(int jobId) throws RemoteException {
+ final int uid = Binder.getCallingUid();
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ JobSchedulerService.this.cancelJob(uid, jobId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * "dumpsys" infrastructure
+ */
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
+ long identityToken = Binder.clearCallingIdentity();
+ try {
+ JobSchedulerService.this.dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(identityToken);
+ }
+ }
+ };
+
+ void dumpInternal(PrintWriter pw) {
+ synchronized (mJobs) {
+ pw.println("Registered jobs:");
+ if (mJobs.size() > 0) {
+ for (JobStatus job : mJobs.getJobs()) {
+ job.dump(pw, " ");
+ }
+ } else {
+ pw.println();
+ pw.println("No jobs scheduled.");
+ }
+ for (StateController controller : mControllers) {
+ pw.println();
+ controller.dumpControllerState(pw);
+ }
+ pw.println();
+ pw.println("Pending");
+ for (JobStatus jobStatus : mPendingJobs) {
+ pw.println(jobStatus.hashCode());
+ }
+ pw.println();
+ pw.println("Active jobs:");
+ for (JobServiceContext jsc : mActiveServices) {
+ if (jsc.isAvailable()) {
+ continue;
+ } else {
+ pw.println(jsc.getRunningJob().hashCode() + " for: " +
+ (SystemClock.elapsedRealtime()
+ - jsc.getExecutionStartTimeElapsed())/1000 + "s " +
+ "timeout: " + jsc.getTimeoutElapsed());
+ }
+ }
+ }
+ pw.println();
+ }
+}
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
new file mode 100644
index 0000000..534faba3
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job;
+
+import android.app.ActivityManager;
+import android.app.job.JobParameters;
+import android.app.job.IJobCallback;
+import android.app.job.IJobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.WorkSource;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.job.controllers.JobStatus;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Handles client binding and lifecycle of a job. A job will only execute one at a time on an
+ * instance of this class.
+ */
+public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
+ private static final boolean DEBUG = true;
+ private static final String TAG = "JobServiceContext";
+ /** Define the maximum # of jobs allowed to run on a service at once. */
+ private static final int defaultMaxActiveJobsPerService =
+ ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
+ /** Amount of time a job is allowed to execute for before being considered timed-out. */
+ private static final long EXECUTING_TIMESLICE_MILLIS = 60 * 1000;
+ /** Amount of time the JobScheduler will wait for a response from an app for a message. */
+ private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
+ /** String prefix for all wakelock names. */
+ private static final String JS_WAKELOCK_PREFIX = "*job*/";
+
+ private static final String[] VERB_STRINGS = {
+ "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_PENDING"
+ };
+
+ // States that a job occupies while interacting with the client.
+ static final int VERB_BINDING = 0;
+ static final int VERB_STARTING = 1;
+ static final int VERB_EXECUTING = 2;
+ static final int VERB_STOPPING = 3;
+
+ // Messages that result from interactions with the client service.
+ /** System timed out waiting for a response. */
+ private static final int MSG_TIMEOUT = 0;
+ /** Received a callback from client. */
+ private static final int MSG_CALLBACK = 1;
+ /** Run through list and start any ready jobs.*/
+ private static final int MSG_SERVICE_BOUND = 2;
+ /** Cancel a job. */
+ private static final int MSG_CANCEL = 3;
+ /** Shutdown the job. Used when the client crashes and we can't die gracefully.*/
+ private static final int MSG_SHUTDOWN_EXECUTION = 4;
+
+ private final Handler mCallbackHandler;
+ /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
+ private final JobCompletedListener mCompletedListener;
+ /** Used for service binding, etc. */
+ private final Context mContext;
+ private PowerManager.WakeLock mWakeLock;
+
+ // Execution state.
+ private JobParameters mParams;
+ @VisibleForTesting
+ int mVerb;
+ private AtomicBoolean mCancelled = new AtomicBoolean();
+
+ /** All the information maintained about the job currently being executed. */
+ private JobStatus mRunningJob;
+ /** Binder to the client service. */
+ IJobService service;
+
+ private final Object mLock = new Object();
+ /** Whether this context is free. */
+ @GuardedBy("mLock")
+ private boolean mAvailable;
+ /** Track start time. */
+ private long mExecutionStartTimeElapsed;
+ /** Track when job will timeout. */
+ private long mTimeoutElapsed;
+
+ JobServiceContext(JobSchedulerService service, Looper looper) {
+ this(service.getContext(), service, looper);
+ }
+
+ @VisibleForTesting
+ JobServiceContext(Context context, JobCompletedListener completedListener, Looper looper) {
+ mContext = context;
+ mCallbackHandler = new JobServiceHandler(looper);
+ mCompletedListener = completedListener;
+ mAvailable = true;
+ }
+
+ /**
+ * Give a job to this context for execution. Callers must first check {@link #isAvailable()}
+ * to make sure this is a valid context.
+ * @param job The status of the job that we are going to run.
+ * @return True if the job is valid and is running. False if the job cannot be executed.
+ */
+ boolean executeRunnableJob(JobStatus job) {
+ synchronized (mLock) {
+ if (!mAvailable) {
+ Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
+ return false;
+ }
+
+ mRunningJob = job;
+ mParams = new JobParameters(job.getJobId(), job.getExtras(), this);
+ mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
+
+ mVerb = VERB_BINDING;
+ final Intent intent = new Intent().setComponent(job.getServiceComponent());
+ boolean binding = mContext.bindServiceAsUser(intent, this,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
+ new UserHandle(job.getUserId()));
+ if (!binding) {
+ if (DEBUG) {
+ Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
+ }
+ mRunningJob = null;
+ mParams = null;
+ mExecutionStartTimeElapsed = 0L;
+ return false;
+ }
+ mAvailable = false;
+ return true;
+ }
+ }
+
+ /** Used externally to query the running job. Will return null if there is no job running. */
+ JobStatus getRunningJob() {
+ return mRunningJob;
+ }
+
+ /** Called externally when a job that was scheduled for execution should be cancelled. */
+ void cancelExecutingJob() {
+ mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
+ }
+
+ /**
+ * @return Whether this context is available to handle incoming work.
+ */
+ boolean isAvailable() {
+ synchronized (mLock) {
+ return mAvailable;
+ }
+ }
+
+ long getExecutionStartTimeElapsed() {
+ return mExecutionStartTimeElapsed;
+ }
+
+ long getTimeoutElapsed() {
+ return mTimeoutElapsed;
+ }
+
+ @Override
+ public void jobFinished(int jobId, boolean reschedule) {
+ if (!verifyCallingUid()) {
+ return;
+ }
+ mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
+ .sendToTarget();
+ }
+
+ @Override
+ public void acknowledgeStopMessage(int jobId, boolean reschedule) {
+ if (!verifyCallingUid()) {
+ return;
+ }
+ mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
+ .sendToTarget();
+ }
+
+ @Override
+ public void acknowledgeStartMessage(int jobId, boolean ongoing) {
+ if (!verifyCallingUid()) {
+ return;
+ }
+ mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget();
+ }
+
+ /**
+ * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
+ * we intend to send to the client - we stop sending work when the service is unbound so until
+ * then we keep the wakelock.
+ * @param name The concrete component name of the service that has been connected.
+ * @param service The IBinder of the Service's communication channel,
+ */
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (!name.equals(mRunningJob.getServiceComponent())) {
+ mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
+ return;
+ }
+ this.service = IJobService.Stub.asInterface(service);
+ // Remove all timeouts.
+ mCallbackHandler.removeMessages(MSG_TIMEOUT);
+ final PowerManager pm =
+ (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ JS_WAKELOCK_PREFIX + mRunningJob.getServiceComponent().getPackageName());
+ mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid()));
+ mWakeLock.setReferenceCounted(false);
+ mWakeLock.acquire();
+ mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
+ }
+
+ /**
+ * If the client service crashes we reschedule this job and clean up.
+ * @param name The concrete component name of the service whose
+ */
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
+ }
+
+ /**
+ * This class is reused across different clients, and passes itself in as a callback. Check
+ * whether the client exercising the callback is the client we expect.
+ * @return True if the binder calling is coming from the client we expect.
+ */
+ private boolean verifyCallingUid() {
+ if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Stale callback received, ignoring.");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Handles the lifecycle of the JobService binding/callbacks, etc. The convention within this
+ * class is to append 'H' to each function name that can only be called on this handler. This
+ * isn't strictly necessary because all of these functions are private, but helps clarity.
+ */
+ private class JobServiceHandler extends Handler {
+ JobServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_SERVICE_BOUND:
+ handleServiceBoundH();
+ break;
+ case MSG_CALLBACK:
+ if (DEBUG) {
+ Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob + " v:" +
+ VERB_STRINGS[mVerb]);
+ }
+ removeMessages(MSG_TIMEOUT);
+
+ if (mVerb == VERB_STARTING) {
+ final boolean workOngoing = message.arg2 == 1;
+ handleStartedH(workOngoing);
+ } else if (mVerb == VERB_EXECUTING ||
+ mVerb == VERB_STOPPING) {
+ final boolean reschedule = message.arg2 == 1;
+ handleFinishedH(reschedule);
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
+ }
+ }
+ break;
+ case MSG_CANCEL:
+ handleCancelH();
+ break;
+ case MSG_TIMEOUT:
+ handleOpTimeoutH();
+ break;
+ case MSG_SHUTDOWN_EXECUTION:
+ closeAndCleanupJobH(true /* needsReschedule */);
+ break;
+ default:
+ Log.e(TAG, "Unrecognised message: " + message);
+ }
+ }
+
+ /** Start the job on the service. */
+ private void handleServiceBoundH() {
+ if (mVerb != VERB_BINDING) {
+ Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
+ + VERB_STRINGS[mVerb]);
+ closeAndCleanupJobH(false /* reschedule */);
+ return;
+ }
+ if (mCancelled.get()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
+ + mRunningJob);
+ }
+ closeAndCleanupJobH(true /* reschedule */);
+ return;
+ }
+ try {
+ mVerb = VERB_STARTING;
+ scheduleOpTimeOut();
+ service.startJob(mParams);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending onStart message to '" +
+ mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
+ }
+ }
+
+ /**
+ * State behaviours.
+ * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout.
+ * _PENDING -> Error
+ * _EXECUTING -> Error
+ * _STOPPING -> Error
+ */
+ private void handleStartedH(boolean workOngoing) {
+ switch (mVerb) {
+ case VERB_STARTING:
+ mVerb = VERB_EXECUTING;
+ if (!workOngoing) {
+ // Job is finished already so fast-forward to handleFinished.
+ handleFinishedH(false);
+ return;
+ }
+ if (mCancelled.get()) {
+ // Cancelled *while* waiting for acknowledgeStartMessage from client.
+ handleCancelH();
+ return;
+ }
+ scheduleOpTimeOut();
+ break;
+ default:
+ Log.e(TAG, "Handling started job but job wasn't starting! Was "
+ + VERB_STRINGS[mVerb] + ".");
+ return;
+ }
+ }
+
+ /**
+ * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done.
+ * _STOPPING -> Successful finish, clean up and notify done.
+ * _STARTING -> Error
+ * _PENDING -> Error
+ */
+ private void handleFinishedH(boolean reschedule) {
+ switch (mVerb) {
+ case VERB_EXECUTING:
+ case VERB_STOPPING:
+ closeAndCleanupJobH(reschedule);
+ break;
+ default:
+ Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
+ "executed. Was " + VERB_STRINGS[mVerb] + ".");
+ }
+ }
+
+ /**
+ * A job can be in various states when a cancel request comes in:
+ * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
+ * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
+ * _STARTING -> Mark as cancelled and wait for
+ * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)}
+ * _EXECUTING -> call {@link #sendStopMessageH}}.
+ * _ENDING -> No point in doing anything here, so we ignore.
+ */
+ private void handleCancelH() {
+ switch (mVerb) {
+ case VERB_BINDING:
+ case VERB_STARTING:
+ mCancelled.set(true);
+ break;
+ case VERB_EXECUTING:
+ sendStopMessageH();
+ break;
+ case VERB_STOPPING:
+ // Nada.
+ break;
+ default:
+ Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
+ break;
+ }
+ }
+
+ /** Process MSG_TIMEOUT here. */
+ private void handleOpTimeoutH() {
+ if (Log.isLoggable(JobSchedulerService.TAG, Log.DEBUG)) {
+ Log.d(TAG, "MSG_TIMEOUT of " +
+ mRunningJob.getServiceComponent().getShortClassName() + " : "
+ + mParams.getJobId());
+ }
+
+ final int jobId = mParams.getJobId();
+ switch (mVerb) {
+ case VERB_STARTING:
+ // Client unresponsive - wedged or failed to respond in time. We don't really
+ // know what happened so let's log it and notify the JobScheduler
+ // FINISHED/NO-RETRY.
+ Log.e(TAG, "No response from client for onStartJob '" +
+ mRunningJob.getServiceComponent().getShortClassName() + "' tId: "
+ + jobId);
+ closeAndCleanupJobH(false /* needsReschedule */);
+ break;
+ case VERB_STOPPING:
+ // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
+ Log.e(TAG, "No response from client for onStopJob, '" +
+ mRunningJob.getServiceComponent().getShortClassName() + "' tId: "
+ + jobId);
+ closeAndCleanupJobH(true /* needsReschedule */);
+ break;
+ case VERB_EXECUTING:
+ // Not an error - client ran out of time.
+ Log.i(TAG, "Client timed out while executing (no jobFinished received)." +
+ " sending onStop. " +
+ mRunningJob.getServiceComponent().getShortClassName() + "' tId: "
+ + jobId);
+ sendStopMessageH();
+ break;
+ default:
+ Log.e(TAG, "Handling timeout for an unknown active job state: "
+ + mRunningJob);
+ return;
+ }
+ }
+
+ /**
+ * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
+ * VERB_STOPPING.
+ */
+ private void sendStopMessageH() {
+ mCallbackHandler.removeMessages(MSG_TIMEOUT);
+ if (mVerb != VERB_EXECUTING) {
+ Log.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
+ closeAndCleanupJobH(false /* reschedule */);
+ return;
+ }
+ try {
+ mVerb = VERB_STOPPING;
+ scheduleOpTimeOut();
+ service.stopJob(mParams);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending onStopJob to client.", e);
+ closeAndCleanupJobH(false /* reschedule */);
+ }
+ }
+
+ /**
+ * The provided job has finished, either by calling
+ * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
+ * or from acknowledging the stop message we sent. Either way, we're done tracking it and
+ * we want to clean up internally.
+ */
+ private void closeAndCleanupJobH(boolean reschedule) {
+ removeMessages(MSG_TIMEOUT);
+ mCompletedListener.onJobCompleted(mRunningJob, reschedule);
+ synchronized (mLock) {
+ mWakeLock.release();
+ mContext.unbindService(JobServiceContext.this);
+ mWakeLock = null;
+ mRunningJob = null;
+ mParams = null;
+ mVerb = -1;
+ mCancelled.set(false);
+ service = null;
+ mAvailable = true;
+ }
+ }
+
+ /**
+ * Called when sending a message to the client, over whose execution we have no control. If we
+ * haven't received a response in a certain amount of time, we want to give up and carry on
+ * with life.
+ */
+ private void scheduleOpTimeOut() {
+ mCallbackHandler.removeMessages(MSG_TIMEOUT);
+
+ final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
+ EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
+ if (DEBUG) {
+ Slog.d(TAG, "Scheduling time out for '" +
+ mRunningJob.getServiceComponent().getShortClassName() + "' tId: " +
+ mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
+ }
+ Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
+ mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
+ mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
new file mode 100644
index 0000000..4ac26c1
--- /dev/null
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job;
+
+import android.content.ComponentName;
+import android.app.job.JobInfo;
+import android.content.Context;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.server.IoThread;
+import com.android.server.job.controllers.JobStatus;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+/**
+ * Maintain a list of classes, and accessor methods/logic for these jobs.
+ * This class offers the following functionality:
+ * - When a job is added, it will determine if the job requirements have changed (update) and
+ * whether the controllers need to be updated.
+ * - Persists JobInfos, figures out when to to rewrite the JobInfo to disk.
+ * - Handles rescheduling of jobs.
+ * - When a periodic job is executed and must be re-added.
+ * - When a job fails and the client requests that it be retried with backoff.
+ * - This class <strong>is not</strong> thread-safe.
+ *
+ * Note on locking:
+ * All callers to this class must <strong>lock on the class object they are calling</strong>.
+ * This is important b/c {@link com.android.server.job.JobStore.WriteJobsMapToDiskRunnable}
+ * and {@link com.android.server.job.JobStore.ReadJobMapFromDiskRunnable} lock on that
+ * object.
+ */
+public class JobStore {
+ private static final String TAG = "JobStore";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG;
+
+ /** Threshold to adjust how often we want to write to the db. */
+ private static final int MAX_OPS_BEFORE_WRITE = 1;
+ final ArraySet<JobStatus> mJobSet;
+ final Context mContext;
+
+ private int mDirtyOperations;
+
+ private static final Object sSingletonLock = new Object();
+ private final AtomicFile mJobsFile;
+ /** Handler backed by IoThread for writing to disk. */
+ private final Handler mIoHandler = IoThread.getHandler();
+ private static JobStore sSingleton;
+
+ /** Used by the {@link JobSchedulerService} to instantiate the JobStore. */
+ static JobStore initAndGet(JobSchedulerService jobManagerService) {
+ synchronized (sSingletonLock) {
+ if (sSingleton == null) {
+ sSingleton = new JobStore(jobManagerService.getContext(),
+ Environment.getDataDirectory(), jobManagerService);
+ }
+ return sSingleton;
+ }
+ }
+
+ @VisibleForTesting
+ public static JobStore initAndGetForTesting(Context context, File dataDir,
+ JobMapReadFinishedListener callback) {
+ return new JobStore(context, dataDir, callback);
+ }
+
+ private JobStore(Context context, File dataDir, JobMapReadFinishedListener callback) {
+ mContext = context;
+ mDirtyOperations = 0;
+
+ File systemDir = new File(dataDir, "system");
+ File jobDir = new File(systemDir, "job");
+ jobDir.mkdirs();
+ mJobsFile = new AtomicFile(new File(jobDir, "jobs.xml"));
+
+ mJobSet = new ArraySet<JobStatus>();
+
+ readJobMapFromDiskAsync(callback);
+ }
+
+ /**
+ * Add a job to the master list, persisting it if necessary. If the JobStatus already exists,
+ * it will be replaced.
+ * @param jobStatus Job to add.
+ * @return Whether or not an equivalent JobStatus was replaced by this operation.
+ */
+ public boolean add(JobStatus jobStatus) {
+ boolean replaced = mJobSet.remove(jobStatus);
+ mJobSet.add(jobStatus);
+ if (jobStatus.isPersisted()) {
+ maybeWriteStatusToDiskAsync();
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Added job status to store: " + jobStatus);
+ }
+ return replaced;
+ }
+
+ /**
+ * Whether this jobStatus object already exists in the JobStore.
+ */
+ public boolean containsJobIdForUid(int jobId, int uId) {
+ for (JobStatus ts : mJobSet) {
+ if (ts.getUid() == uId && ts.getJobId() == jobId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int size() {
+ return mJobSet.size();
+ }
+
+ /**
+ * Remove the provided job. Will also delete the job if it was persisted.
+ * @return Whether or not the job existed to be removed.
+ */
+ public boolean remove(JobStatus jobStatus) {
+ boolean removed = mJobSet.remove(jobStatus);
+ if (!removed) {
+ if (DEBUG) {
+ Slog.d(TAG, "Couldn't remove job: didn't exist: " + jobStatus);
+ }
+ return false;
+ }
+ maybeWriteStatusToDiskAsync();
+ return removed;
+ }
+
+ @VisibleForTesting
+ public void clear() {
+ mJobSet.clear();
+ maybeWriteStatusToDiskAsync();
+ }
+
+ public List<JobStatus> getJobsByUser(int userHandle) {
+ List<JobStatus> matchingJobs = new ArrayList<JobStatus>();
+ Iterator<JobStatus> it = mJobSet.iterator();
+ while (it.hasNext()) {
+ JobStatus ts = it.next();
+ if (UserHandle.getUserId(ts.getUid()) == userHandle) {
+ matchingJobs.add(ts);
+ }
+ }
+ return matchingJobs;
+ }
+
+ /**
+ * @param uid Uid of the requesting app.
+ * @return All JobStatus objects for a given uid from the master list.
+ */
+ public List<JobStatus> getJobsByUid(int uid) {
+ List<JobStatus> matchingJobs = new ArrayList<JobStatus>();
+ Iterator<JobStatus> it = mJobSet.iterator();
+ while (it.hasNext()) {
+ JobStatus ts = it.next();
+ if (ts.getUid() == uid) {
+ matchingJobs.add(ts);
+ }
+ }
+ return matchingJobs;
+ }
+
+ /**
+ * @param uid Uid of the requesting app.
+ * @param jobId Job id, specified at schedule-time.
+ * @return the JobStatus that matches the provided uId and jobId, or null if none found.
+ */
+ public JobStatus getJobByUidAndJobId(int uid, int jobId) {
+ Iterator<JobStatus> it = mJobSet.iterator();
+ while (it.hasNext()) {
+ JobStatus ts = it.next();
+ if (ts.getUid() == uid && ts.getJobId() == jobId) {
+ return ts;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return The live array of JobStatus objects.
+ */
+ public ArraySet<JobStatus> getJobs() {
+ return mJobSet;
+ }
+
+ /** Version of the db schema. */
+ private static final int JOBS_FILE_VERSION = 0;
+ /** Tag corresponds to constraints this job needs. */
+ private static final String XML_TAG_PARAMS_CONSTRAINTS = "constraints";
+ /** Tag corresponds to execution parameters. */
+ private static final String XML_TAG_PERIODIC = "periodic";
+ private static final String XML_TAG_ONEOFF = "one-off";
+ private static final String XML_TAG_EXTRAS = "extras";
+
+ /**
+ * Every time the state changes we write all the jobs in one swath, instead of trying to
+ * track incremental changes.
+ * @return Whether the operation was successful. This will only fail for e.g. if the system is
+ * low on storage. If this happens, we continue as normal
+ */
+ private void maybeWriteStatusToDiskAsync() {
+ mDirtyOperations++;
+ if (mDirtyOperations >= MAX_OPS_BEFORE_WRITE) {
+ if (DEBUG) {
+ Slog.v(TAG, "Writing jobs to disk.");
+ }
+ mIoHandler.post(new WriteJobsMapToDiskRunnable());
+ }
+ }
+
+ private void readJobMapFromDiskAsync(JobMapReadFinishedListener callback) {
+ mIoHandler.post(new ReadJobMapFromDiskRunnable(callback));
+ }
+
+ public void readJobMapFromDisk(JobMapReadFinishedListener callback) {
+ new ReadJobMapFromDiskRunnable(callback).run();
+ }
+
+ /**
+ * Runnable that writes {@link #mJobSet} out to xml.
+ * NOTE: This Runnable locks on JobStore.this
+ */
+ private class WriteJobsMapToDiskRunnable implements Runnable {
+ @Override
+ public void run() {
+ final long startElapsed = SystemClock.elapsedRealtime();
+ List<JobStatus> mStoreCopy = new ArrayList<JobStatus>();
+ synchronized (JobStore.this) {
+ // Copy over the jobs so we can release the lock before writing.
+ for (JobStatus jobStatus : mJobSet) {
+ JobStatus copy = new JobStatus(jobStatus.getJob(), jobStatus.getUid(),
+ jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed());
+ mStoreCopy.add(copy);
+ }
+ }
+ writeJobsMapImpl(mStoreCopy);
+ if (JobSchedulerService.DEBUG) {
+ Slog.v(TAG, "Finished writing, took " + (SystemClock.elapsedRealtime()
+ - startElapsed) + "ms");
+ }
+ }
+
+ private void writeJobsMapImpl(List<JobStatus> jobList) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(baos, "utf-8");
+ out.startDocument(null, true);
+ out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ out.startTag(null, "job-info");
+ out.attribute(null, "version", Integer.toString(JOBS_FILE_VERSION));
+ for (JobStatus jobStatus : jobList) {
+ if (DEBUG) {
+ Slog.d(TAG, "Saving job " + jobStatus.getJobId());
+ }
+ out.startTag(null, "job");
+ addIdentifierAttributesToJobTag(out, jobStatus);
+ writeConstraintsToXml(out, jobStatus);
+ writeExecutionCriteriaToXml(out, jobStatus);
+ writeBundleToXml(jobStatus.getExtras(), out);
+ out.endTag(null, "job");
+ }
+ out.endTag(null, "job-info");
+ out.endDocument();
+
+ // Write out to disk in one fell sweep.
+ FileOutputStream fos = mJobsFile.startWrite();
+ fos.write(baos.toByteArray());
+ mJobsFile.finishWrite(fos);
+ mDirtyOperations = 0;
+ } catch (IOException e) {
+ if (DEBUG) {
+ Slog.v(TAG, "Error writing out job data.", e);
+ }
+ } catch (XmlPullParserException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "Error persisting bundle.", e);
+ }
+ }
+ }
+
+ /** Write out a tag with data comprising the required fields of this job and its client. */
+ private void addIdentifierAttributesToJobTag(XmlSerializer out, JobStatus jobStatus)
+ throws IOException {
+ out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId()));
+ out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName());
+ out.attribute(null, "class", jobStatus.getServiceComponent().getClassName());
+ out.attribute(null, "uid", Integer.toString(jobStatus.getUid()));
+ }
+
+ private void writeBundleToXml(PersistableBundle extras, XmlSerializer out)
+ throws IOException, XmlPullParserException {
+ out.startTag(null, XML_TAG_EXTRAS);
+ extras.saveToXml(out);
+ out.endTag(null, XML_TAG_EXTRAS);
+ }
+ /**
+ * Write out a tag with data identifying this job's constraints. If the constraint isn't here
+ * it doesn't apply.
+ */
+ private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException {
+ out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS);
+ if (jobStatus.hasUnmeteredConstraint()) {
+ out.attribute(null, "unmetered", Boolean.toString(true));
+ }
+ if (jobStatus.hasConnectivityConstraint()) {
+ out.attribute(null, "connectivity", Boolean.toString(true));
+ }
+ if (jobStatus.hasIdleConstraint()) {
+ out.attribute(null, "idle", Boolean.toString(true));
+ }
+ if (jobStatus.hasChargingConstraint()) {
+ out.attribute(null, "charging", Boolean.toString(true));
+ }
+ out.endTag(null, XML_TAG_PARAMS_CONSTRAINTS);
+ }
+
+ private void writeExecutionCriteriaToXml(XmlSerializer out, JobStatus jobStatus)
+ throws IOException {
+ final JobInfo job = jobStatus.getJob();
+ if (jobStatus.getJob().isPeriodic()) {
+ out.startTag(null, XML_TAG_PERIODIC);
+ out.attribute(null, "period", Long.toString(job.getIntervalMillis()));
+ } else {
+ out.startTag(null, XML_TAG_ONEOFF);
+ }
+
+ if (jobStatus.hasDeadlineConstraint()) {
+ // Wall clock deadline.
+ final long deadlineWallclock = System.currentTimeMillis() +
+ (jobStatus.getLatestRunTimeElapsed() - SystemClock.elapsedRealtime());
+ out.attribute(null, "deadline", Long.toString(deadlineWallclock));
+ }
+ if (jobStatus.hasTimingDelayConstraint()) {
+ final long delayWallclock = System.currentTimeMillis() +
+ (jobStatus.getEarliestRunTime() - SystemClock.elapsedRealtime());
+ out.attribute(null, "delay", Long.toString(delayWallclock));
+ }
+
+ // Only write out back-off policy if it differs from the default.
+ // This also helps the case where the job is idle -> these aren't allowed to specify
+ // back-off.
+ if (jobStatus.getJob().getInitialBackoffMillis() != JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS
+ || jobStatus.getJob().getBackoffPolicy() != JobInfo.DEFAULT_BACKOFF_POLICY) {
+ out.attribute(null, "backoff-policy", Integer.toString(job.getBackoffPolicy()));
+ out.attribute(null, "initial-backoff", Long.toString(job.getInitialBackoffMillis()));
+ }
+ if (job.isPeriodic()) {
+ out.endTag(null, XML_TAG_PERIODIC);
+ } else {
+ out.endTag(null, XML_TAG_ONEOFF);
+ }
+ }
+ }
+
+ /**
+ * Runnable that reads list of persisted job from xml.
+ * NOTE: This Runnable locks on JobStore.this
+ */
+ private class ReadJobMapFromDiskRunnable implements Runnable {
+ private JobMapReadFinishedListener mCallback;
+ public ReadJobMapFromDiskRunnable(JobMapReadFinishedListener callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void run() {
+ try {
+ List<JobStatus> jobs;
+ FileInputStream fis = mJobsFile.openRead();
+ synchronized (JobStore.this) {
+ jobs = readJobMapImpl(fis);
+ }
+ fis.close();
+ if (jobs != null) {
+ mCallback.onJobMapReadFinished(jobs);
+ }
+ } catch (FileNotFoundException e) {
+ if (JobSchedulerService.DEBUG) {
+ Slog.d(TAG, "Could not find jobs file, probably there was nothing to load.");
+ }
+ } catch (XmlPullParserException e) {
+ if (JobSchedulerService.DEBUG) {
+ Slog.d(TAG, "Error parsing xml.", e);
+ }
+ } catch (IOException e) {
+ if (JobSchedulerService.DEBUG) {
+ Slog.d(TAG, "Error parsing xml.", e);
+ }
+ }
+ }
+
+ private List<JobStatus> readJobMapImpl(FileInputStream fis) throws XmlPullParserException, IOException {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.START_TAG &&
+ eventType != XmlPullParser.END_DOCUMENT) {
+ eventType = parser.next();
+ Slog.d(TAG, parser.getName());
+ }
+ if (eventType == XmlPullParser.END_DOCUMENT) {
+ if (DEBUG) {
+ Slog.d(TAG, "No persisted jobs.");
+ }
+ return null;
+ }
+
+ String tagName = parser.getName();
+ if ("job-info".equals(tagName)) {
+ final List<JobStatus> jobs = new ArrayList<JobStatus>();
+ // Read in version info.
+ try {
+ int version = Integer.valueOf(parser.getAttributeValue(null, "version"));
+ if (version != JOBS_FILE_VERSION) {
+ Slog.d(TAG, "Invalid version number, aborting jobs file read.");
+ return null;
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Invalid version number, aborting jobs file read.");
+ return null;
+ }
+ eventType = parser.next();
+ do {
+ // Read each <job/>
+ if (eventType == XmlPullParser.START_TAG) {
+ tagName = parser.getName();
+ // Start reading job.
+ if ("job".equals(tagName)) {
+ JobStatus persistedJob = restoreJobFromXml(parser);
+ if (persistedJob != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Read out " + persistedJob);
+ }
+ jobs.add(persistedJob);
+ } else {
+ Slog.d(TAG, "Error reading job from file.");
+ }
+ }
+ }
+ eventType = parser.next();
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+ return jobs;
+ }
+ return null;
+ }
+
+ /**
+ * @param parser Xml parser at the beginning of a "<job/>" tag. The next "parser.next()" call
+ * will take the parser into the body of the job tag.
+ * @return Newly instantiated job holding all the information we just read out of the xml tag.
+ */
+ private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ JobInfo.Builder jobBuilder;
+ int uid;
+
+ // Read out job identifier attributes.
+ try {
+ jobBuilder = buildBuilderFromXml(parser);
+ uid = Integer.valueOf(parser.getAttributeValue(null, "uid"));
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Error parsing job's required fields, skipping");
+ return null;
+ }
+
+ int eventType;
+ // Read out constraints tag.
+ do {
+ eventType = parser.next();
+ } while (eventType == XmlPullParser.TEXT); // Push through to next START_TAG.
+
+ if (!(eventType == XmlPullParser.START_TAG &&
+ XML_TAG_PARAMS_CONSTRAINTS.equals(parser.getName()))) {
+ // Expecting a <constraints> start tag.
+ return null;
+ }
+ try {
+ buildConstraintsFromXml(jobBuilder, parser);
+ } catch (NumberFormatException e) {
+ Slog.d(TAG, "Error reading constraints, skipping.");
+ return null;
+ }
+ parser.next(); // Consume </constraints>
+
+ // Read out execution parameters tag.
+ do {
+ eventType = parser.next();
+ } while (eventType == XmlPullParser.TEXT);
+ if (eventType != XmlPullParser.START_TAG) {
+ return null;
+ }
+
+ Pair<Long, Long> runtimes;
+ try {
+ runtimes = buildExecutionTimesFromXml(parser);
+ } catch (NumberFormatException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "Error parsing execution time parameters, skipping.");
+ }
+ return null;
+ }
+
+ if (XML_TAG_PERIODIC.equals(parser.getName())) {
+ try {
+ String val = parser.getAttributeValue(null, "period");
+ jobBuilder.setPeriodic(Long.valueOf(val));
+ } catch (NumberFormatException e) {
+ Slog.d(TAG, "Error reading periodic execution criteria, skipping.");
+ return null;
+ }
+ } else if (XML_TAG_ONEOFF.equals(parser.getName())) {
+ try {
+ if (runtimes.first != JobStatus.NO_EARLIEST_RUNTIME) {
+ jobBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime());
+ }
+ if (runtimes.second != JobStatus.NO_LATEST_RUNTIME) {
+ jobBuilder.setOverrideDeadline(
+ runtimes.second - SystemClock.elapsedRealtime());
+ }
+ } catch (NumberFormatException e) {
+ Slog.d(TAG, "Error reading job execution criteria, skipping.");
+ return null;
+ }
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Invalid parameter tag, skipping - " + parser.getName());
+ }
+ // Expecting a parameters start tag.
+ return null;
+ }
+ maybeBuildBackoffPolicyFromXml(jobBuilder, parser);
+
+ parser.nextTag(); // Consume parameters end tag.
+
+ // Read out extras Bundle.
+ do {
+ eventType = parser.next();
+ } while (eventType == XmlPullParser.TEXT);
+ if (!(eventType == XmlPullParser.START_TAG && XML_TAG_EXTRAS.equals(parser.getName()))) {
+ if (DEBUG) {
+ Slog.d(TAG, "Error reading extras, skipping.");
+ }
+ return null;
+ }
+
+ PersistableBundle extras = PersistableBundle.restoreFromXml(parser);
+ jobBuilder.setExtras(extras);
+ parser.nextTag(); // Consume </extras>
+
+ return new JobStatus(jobBuilder.build(), uid, runtimes.first, runtimes.second);
+ }
+
+ private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException {
+ // Pull out required fields from <job> attributes.
+ int jobId = Integer.valueOf(parser.getAttributeValue(null, "jobid"));
+ String packageName = parser.getAttributeValue(null, "package");
+ String className = parser.getAttributeValue(null, "class");
+ ComponentName cname = new ComponentName(packageName, className);
+
+ return new JobInfo.Builder(jobId, cname);
+ }
+
+ private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) {
+ String val = parser.getAttributeValue(null, "unmetered");
+ if (val != null) {
+ jobBuilder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED);
+ }
+ val = parser.getAttributeValue(null, "connectivity");
+ if (val != null) {
+ jobBuilder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY);
+ }
+ val = parser.getAttributeValue(null, "idle");
+ if (val != null) {
+ jobBuilder.setRequiresDeviceIdle(true);
+ }
+ val = parser.getAttributeValue(null, "charging");
+ if (val != null) {
+ jobBuilder.setRequiresCharging(true);
+ }
+ }
+
+ /**
+ * Builds the back-off policy out of the params tag. These attributes may not exist, depending
+ * on whether the back-off was set when the job was first scheduled.
+ */
+ private void maybeBuildBackoffPolicyFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) {
+ String val = parser.getAttributeValue(null, "initial-backoff");
+ if (val != null) {
+ long initialBackoff = Long.valueOf(val);
+ val = parser.getAttributeValue(null, "backoff-policy");
+ int backoffPolicy = Integer.valueOf(val); // Will throw NFE which we catch higher up.
+ jobBuilder.setBackoffCriteria(initialBackoff, backoffPolicy);
+ }
+ }
+
+ /**
+ * Convenience function to read out and convert deadline and delay from xml into elapsed real
+ * time.
+ * @return A {@link android.util.Pair}, where the first value is the earliest elapsed runtime
+ * and the second is the latest elapsed runtime.
+ */
+ private Pair<Long, Long> buildExecutionTimesFromXml(XmlPullParser parser)
+ throws NumberFormatException {
+ // Pull out execution time data.
+ final long nowWallclock = System.currentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+
+ long earliestRunTimeElapsed = JobStatus.NO_EARLIEST_RUNTIME;
+ long latestRunTimeElapsed = JobStatus.NO_LATEST_RUNTIME;
+ String val = parser.getAttributeValue(null, "deadline");
+ if (val != null) {
+ long latestRuntimeWallclock = Long.valueOf(val);
+ long maxDelayElapsed =
+ Math.max(latestRuntimeWallclock - nowWallclock, 0);
+ latestRunTimeElapsed = nowElapsed + maxDelayElapsed;
+ }
+ val = parser.getAttributeValue(null, "delay");
+ if (val != null) {
+ long earliestRuntimeWallclock = Long.valueOf(val);
+ long minDelayElapsed =
+ Math.max(earliestRuntimeWallclock - nowWallclock, 0);
+ earliestRunTimeElapsed = nowElapsed + minDelayElapsed;
+
+ }
+ return Pair.create(earliestRunTimeElapsed, latestRunTimeElapsed);
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/job/StateChangedListener.java b/services/core/java/com/android/server/job/StateChangedListener.java
new file mode 100644
index 0000000..90c203a
--- /dev/null
+++ b/services/core/java/com/android/server/job/StateChangedListener.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.server.job;
+
+import com.android.server.job.controllers.JobStatus;
+
+/**
+ * Interface through which a {@link com.android.server.job.controllers.StateController} informs
+ * the {@link com.android.server.job.JobSchedulerService} that there are some tasks potentially
+ * ready to be run.
+ */
+public interface StateChangedListener {
+ /**
+ * Called by the controller to notify the JobManager that it should check on the state of a
+ * task.
+ */
+ public void onControllerStateChanged();
+
+ /**
+ * Called by the controller to notify the JobManager that regardless of the state of the task,
+ * it must be run immediately.
+ * @param jobStatus The state of the task which is to be run immediately.
+ */
+ public void onRunJobNow(JobStatus jobStatus);
+}
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
new file mode 100644
index 0000000..4aef2d31
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryProperty;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.BatteryService;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateChangedListener;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Simple controller that tracks whether the phone is charging or not. The phone is considered to
+ * be charging when it's been plugged in for more than two minutes, and the system has broadcast
+ * ACTION_BATTERY_OK.
+ */
+public class BatteryController extends StateController {
+ private static final String TAG = "JobScheduler.Batt";
+
+ private static final Object sCreationLock = new Object();
+ private static volatile BatteryController sController;
+ private static final String ACTION_CHARGING_STABLE =
+ "com.android.server.task.controllers.BatteryController.ACTION_CHARGING_STABLE";
+ /** Wait this long after phone is plugged in before doing any work. */
+ private static final long STABLE_CHARGING_THRESHOLD_MILLIS = 2 * 60 * 1000; // 2 minutes.
+
+ private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
+ private ChargingTracker mChargeTracker;
+
+ public static BatteryController get(JobSchedulerService taskManagerService) {
+ synchronized (sCreationLock) {
+ if (sController == null) {
+ sController = new BatteryController(taskManagerService,
+ taskManagerService.getContext());
+ }
+ }
+ return sController;
+ }
+
+ @VisibleForTesting
+ public ChargingTracker getTracker() {
+ return mChargeTracker;
+ }
+
+ @VisibleForTesting
+ public static BatteryController getForTesting(StateChangedListener stateChangedListener,
+ Context context) {
+ return new BatteryController(stateChangedListener, context);
+ }
+
+ private BatteryController(StateChangedListener stateChangedListener, Context context) {
+ super(stateChangedListener, context);
+ mChargeTracker = new ChargingTracker();
+ mChargeTracker.startTracking();
+ }
+
+ @Override
+ public void maybeStartTrackingJob(JobStatus taskStatus) {
+ if (taskStatus.hasChargingConstraint()) {
+ final boolean isOnStablePower = mChargeTracker.isOnStablePower();
+ synchronized (mTrackedTasks) {
+ mTrackedTasks.add(taskStatus);
+ taskStatus.chargingConstraintSatisfied.set(isOnStablePower);
+ }
+ if (isOnStablePower) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+ }
+
+ @Override
+ public void maybeStopTrackingJob(JobStatus taskStatus) {
+ if (taskStatus.hasChargingConstraint()) {
+ synchronized (mTrackedTasks) {
+ mTrackedTasks.remove(taskStatus);
+ }
+ }
+ }
+
+ private void maybeReportNewChargingState() {
+ final boolean stablePower = mChargeTracker.isOnStablePower();
+ if (DEBUG) {
+ Slog.d(TAG, "maybeReportNewChargingState: " + stablePower);
+ }
+ boolean reportChange = false;
+ synchronized (mTrackedTasks) {
+ for (JobStatus ts : mTrackedTasks) {
+ boolean previous = ts.chargingConstraintSatisfied.getAndSet(stablePower);
+ if (previous != stablePower) {
+ reportChange = true;
+ }
+ }
+ }
+ if (reportChange) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+
+ public class ChargingTracker extends BroadcastReceiver {
+ private final AlarmManager mAlarm;
+ private final PendingIntent mStableChargingTriggerIntent;
+ /**
+ * Track whether we're "charging", where charging means that we're ready to commit to
+ * doing work.
+ */
+ private boolean mCharging;
+ /** Keep track of whether the battery is charged enough that we want to do work. */
+ private boolean mBatteryHealthy;
+
+ public ChargingTracker() {
+ mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ Intent intent = new Intent(ACTION_CHARGING_STABLE);
+ mStableChargingTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ }
+
+ public void startTracking() {
+ IntentFilter filter = new IntentFilter();
+
+ // Battery health.
+ filter.addAction(Intent.ACTION_BATTERY_LOW);
+ filter.addAction(Intent.ACTION_BATTERY_OKAY);
+ // Charging/not charging.
+ filter.addAction(Intent.ACTION_POWER_CONNECTED);
+ filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
+ // Charging stable.
+ filter.addAction(ACTION_CHARGING_STABLE);
+ mContext.registerReceiver(this, filter);
+
+ // Initialise tracker state.
+ BatteryService batteryService = (BatteryService) ServiceManager.getService("battery");
+ if (batteryService != null) {
+ mBatteryHealthy = !batteryService.getBatteryLevelLow();
+ mCharging = batteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+ } else {
+ // Unavailable for some reason, we default to false and let ACTION_BATTERY_[OK,LOW]
+ // sort it out.
+ }
+ }
+
+ boolean isOnStablePower() {
+ return mCharging && mBatteryHealthy;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onReceiveInternal(intent);
+ }
+
+ @VisibleForTesting
+ public void onReceiveInternal(Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_BATTERY_LOW.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Battery life too low to do work. @ "
+ + SystemClock.elapsedRealtime());
+ }
+ // If we get this action, the battery is discharging => it isn't plugged in so
+ // there's no work to cancel. We track this variable for the case where it is
+ // charging, but hasn't been for long enough to be healthy.
+ mBatteryHealthy = false;
+ } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Battery life healthy enough to do work. @ "
+ + SystemClock.elapsedRealtime());
+ }
+ mBatteryHealthy = true;
+ maybeReportNewChargingState();
+ } else if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received charging intent, setting alarm for "
+ + STABLE_CHARGING_THRESHOLD_MILLIS);
+ }
+ // Set up an alarm for ACTION_CHARGING_STABLE - we don't want to kick off tasks
+ // here if the user unplugs the phone immediately.
+ mAlarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + STABLE_CHARGING_THRESHOLD_MILLIS,
+ mStableChargingTriggerIntent);
+ mCharging = true;
+ } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Disconnected from power, cancelling any set alarms.");
+ }
+ // If an alarm is set, breathe a sigh of relief and cancel it - crisis averted.
+ mAlarm.cancel(mStableChargingTriggerIntent);
+ mCharging = false;
+ maybeReportNewChargingState();
+ }else if (ACTION_CHARGING_STABLE.equals(action)) {
+ // Here's where we actually do the notify for a task being ready.
+ if (DEBUG) {
+ Slog.d(TAG, "Battery connected fired @ " + SystemClock.elapsedRealtime()
+ + " charging: " + mCharging);
+ }
+ if (mCharging) { // Should never receive this intent if mCharging is false.
+ maybeReportNewChargingState();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void dumpControllerState(PrintWriter pw) {
+ pw.println("Batt.");
+ pw.println("Stable power: " + mChargeTracker.isOnStablePower());
+ synchronized (mTrackedTasks) {
+ Iterator<JobStatus> it = mTrackedTasks.iterator();
+ if (it.hasNext()) {
+ pw.print(String.valueOf(it.next().hashCode()));
+ }
+ while (it.hasNext()) {
+ pw.print("," + String.valueOf(it.next().hashCode()));
+ }
+ pw.println();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
new file mode 100644
index 0000000..daba0d9
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.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 com.android.server.job.controllers;
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.server.ConnectivityService;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateChangedListener;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Handles changes in connectivity.
+ * We are only interested in metered vs. unmetered networks, and we're interested in them on a
+ * per-user basis.
+ */
+public class ConnectivityController extends StateController implements
+ ConnectivityManager.OnNetworkActiveListener {
+ private static final String TAG = "JobScheduler.Conn";
+
+ private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>();
+ private final BroadcastReceiver mConnectivityChangedReceiver =
+ new ConnectivityChangedReceiver();
+ /** Singleton. */
+ private static ConnectivityController mSingleton;
+ private static Object sCreationLock = new Object();
+ /** Track whether the latest active network is metered. */
+ private boolean mNetworkUnmetered;
+ /** Track whether the latest active network is connected. */
+ private boolean mNetworkConnected;
+
+ public static ConnectivityController get(JobSchedulerService jms) {
+ synchronized (sCreationLock) {
+ if (mSingleton == null) {
+ mSingleton = new ConnectivityController(jms, jms.getContext());
+ }
+ return mSingleton;
+ }
+ }
+
+ private ConnectivityController(StateChangedListener stateChangedListener, Context context) {
+ super(stateChangedListener, context);
+ // Register connectivity changed BR.
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ mContext.registerReceiverAsUser(
+ mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null);
+ ConnectivityService cs =
+ (ConnectivityService)ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ if (cs != null) {
+ if (cs.getActiveNetworkInfo() != null) {
+ mNetworkConnected = cs.getActiveNetworkInfo().isConnected();
+ }
+ mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered();
+ }
+ }
+
+ @Override
+ public void maybeStartTrackingJob(JobStatus jobStatus) {
+ if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
+ synchronized (mTrackedJobs) {
+ jobStatus.connectivityConstraintSatisfied.set(mNetworkConnected);
+ jobStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered);
+ mTrackedJobs.add(jobStatus);
+ }
+ }
+ }
+
+ @Override
+ public void maybeStopTrackingJob(JobStatus jobStatus) {
+ if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
+ synchronized (mTrackedJobs) {
+ mTrackedJobs.remove(jobStatus);
+ }
+ }
+ }
+
+ /**
+ * @param userId Id of the user for whom we are updating the connectivity state.
+ */
+ private void updateTrackedJobs(int userId) {
+ synchronized (mTrackedJobs) {
+ boolean changed = false;
+ for (JobStatus js : mTrackedJobs) {
+ if (js.getUserId() != userId) {
+ continue;
+ }
+ boolean prevIsConnected =
+ js.connectivityConstraintSatisfied.getAndSet(mNetworkConnected);
+ boolean prevIsMetered = js.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered);
+ if (prevIsConnected != mNetworkConnected || prevIsMetered != mNetworkUnmetered) {
+ changed = true;
+ }
+ }
+ if (changed) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ }
+ }
+
+ /**
+ * We know the network has just come up. We want to run any jobs that are ready.
+ */
+ public synchronized void onNetworkActive() {
+ synchronized (mTrackedJobs) {
+ for (JobStatus js : mTrackedJobs) {
+ if (js.isReady()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Running " + js + " due to network activity.");
+ }
+ mStateChangedListener.onRunJobNow(js);
+ }
+ }
+ }
+ }
+
+ class ConnectivityChangedReceiver extends BroadcastReceiver {
+ /**
+ * We'll receive connectivity changes for each user here, which we process independently.
+ * We are only interested in the active network here. We're only interested in the active
+ * network, b/c the end result of this will be for apps to try to hit the network.
+ * @param context The Context in which the receiver is running.
+ * @param intent The Intent being received.
+ */
+ // TODO: Test whether this will be called twice for each user.
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Received connectivity event: " + intent.getAction() + " u"
+ + context.getUserId());
+ }
+ final String action = intent.getAction();
+ if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ final int networkType =
+ intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
+ ConnectivityManager.TYPE_NONE);
+ // Connectivity manager for THIS context - important!
+ final ConnectivityManager connManager = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
+ final int userid = context.getUserId();
+ // This broadcast gets sent a lot, only update if the active network has changed.
+ if (activeNetwork == null) {
+ mNetworkUnmetered = false;
+ mNetworkConnected = false;
+ updateTrackedJobs(userid);
+ } else if (activeNetwork.getType() == networkType) {
+ mNetworkUnmetered = false;
+ mNetworkConnected = !intent.getBooleanExtra(
+ ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ if (mNetworkConnected) { // No point making the call if we know there's no conn.
+ mNetworkUnmetered = !connManager.isActiveNetworkMetered();
+ }
+ updateTrackedJobs(userid);
+ }
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Unrecognised action in intent: " + action);
+ }
+ }
+ }
+ };
+
+ @Override
+ public void dumpControllerState(PrintWriter pw) {
+ pw.println("Conn.");
+ pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered);
+ for (JobStatus js: mTrackedJobs) {
+ pw.println(String.valueOf(js.hashCode()).substring(0, 3) + ".."
+ + ": C=" + js.hasConnectivityConstraint()
+ + ", UM=" + js.hasUnmeteredConstraint());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
new file mode 100644
index 0000000..07ffe4d
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateChangedListener;
+
+public class IdleController extends StateController {
+ private static final String TAG = "IdleController";
+
+ // Policy: we decide that we're "idle" if the device has been unused /
+ // screen off or dreaming for at least this long
+ private static final long INACTIVITY_IDLE_THRESHOLD = 71 * 60 * 1000; // millis; 71 min
+ private static final long IDLE_WINDOW_SLOP = 5 * 60 * 1000; // 5 minute window, to be nice
+
+ private static final String ACTION_TRIGGER_IDLE =
+ "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE";
+
+ final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
+ IdlenessTracker mIdleTracker;
+
+ // Singleton factory
+ private static Object sCreationLock = new Object();
+ private static volatile IdleController sController;
+
+ public static IdleController get(JobSchedulerService service) {
+ synchronized (sCreationLock) {
+ if (sController == null) {
+ sController = new IdleController(service, service.getContext());
+ }
+ return sController;
+ }
+ }
+
+ private IdleController(StateChangedListener stateChangedListener, Context context) {
+ super(stateChangedListener, context);
+ initIdleStateTracking();
+ }
+
+ /**
+ * StateController interface
+ */
+ @Override
+ public void maybeStartTrackingJob(JobStatus taskStatus) {
+ if (taskStatus.hasIdleConstraint()) {
+ synchronized (mTrackedTasks) {
+ mTrackedTasks.add(taskStatus);
+ taskStatus.idleConstraintSatisfied.set(mIdleTracker.isIdle());
+ }
+ }
+ }
+
+ @Override
+ public void maybeStopTrackingJob(JobStatus taskStatus) {
+ synchronized (mTrackedTasks) {
+ mTrackedTasks.remove(taskStatus);
+ }
+ }
+
+ /**
+ * Interaction with the task manager service
+ */
+ void reportNewIdleState(boolean isIdle) {
+ synchronized (mTrackedTasks) {
+ for (JobStatus task : mTrackedTasks) {
+ task.idleConstraintSatisfied.set(isIdle);
+ }
+ }
+ mStateChangedListener.onControllerStateChanged();
+ }
+
+ /**
+ * Idle state tracking, and messaging with the task manager when
+ * significant state changes occur
+ */
+ private void initIdleStateTracking() {
+ mIdleTracker = new IdlenessTracker();
+ mIdleTracker.startTracking();
+ }
+
+ class IdlenessTracker extends BroadcastReceiver {
+ private AlarmManager mAlarm;
+ private PendingIntent mIdleTriggerIntent;
+ boolean mIdle;
+
+ public IdlenessTracker() {
+ mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+
+ Intent intent = new Intent(ACTION_TRIGGER_IDLE);
+ intent.setComponent(new ComponentName(mContext, this.getClass()));
+ mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+
+ // at boot we presume that the user has just "interacted" with the
+ // device in some meaningful way
+ mIdle = false;
+ }
+
+ public boolean isIdle() {
+ return mIdle;
+ }
+
+ public void startTracking() {
+ IntentFilter filter = new IntentFilter();
+
+ // Screen state
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+
+ // Dreaming state
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+
+ mContext.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ if (action.equals(Intent.ACTION_SCREEN_ON)
+ || action.equals(Intent.ACTION_DREAMING_STOPPED)) {
+ // possible transition to not-idle
+ if (mIdle) {
+ if (DEBUG) {
+ Slog.v(TAG, "exiting idle : " + action);
+ }
+ mAlarm.cancel(mIdleTriggerIntent);
+ mIdle = false;
+ reportNewIdleState(mIdle);
+ }
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)
+ || action.equals(Intent.ACTION_DREAMING_STARTED)) {
+ // when the screen goes off or dreaming starts, we schedule the
+ // alarm that will tell us when we have decided the device is
+ // truly idle.
+ long when = SystemClock.elapsedRealtime() + INACTIVITY_IDLE_THRESHOLD;
+ if (DEBUG) {
+ Slog.v(TAG, "Scheduling idle : " + action + " when=" + when);
+ }
+ mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ when, IDLE_WINDOW_SLOP, mIdleTriggerIntent);
+ } else if (action.equals(ACTION_TRIGGER_IDLE)) {
+ // idle time starts now
+ if (!mIdle) {
+ if (DEBUG) {
+ Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime());
+ }
+ mIdle = true;
+ reportNewIdleState(mIdle);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void dumpControllerState(PrintWriter pw) {
+
+ }
+}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
new file mode 100644
index 0000000..15a6b25
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers;
+
+import android.app.job.JobInfo;
+import android.content.ComponentName;
+import android.os.PersistableBundle;
+import android.os.SystemClock;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Uniquely identifies a job internally.
+ * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler.
+ * Contains current state of the requirements of the job, as well as a function to evaluate
+ * whether it's ready to run.
+ * This object is shared among the various controllers - hence why the different fields are atomic.
+ * This isn't strictly necessary because each controller is only interested in a specific field,
+ * and the receivers that are listening for global state change will all run on the main looper,
+ * but we don't enforce that so this is safer.
+ * @hide
+ */
+public class JobStatus {
+ public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
+ public static final long NO_EARLIEST_RUNTIME = 0L;
+
+ final JobInfo job;
+ final int uId;
+
+ /** At reschedule time we need to know whether to update job on disk. */
+ final boolean persisted;
+
+ // Constraints.
+ final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean unmeteredConstraintSatisfied = new AtomicBoolean();
+ final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
+
+ /**
+ * Earliest point in the future at which this job will be eligible to run. A value of 0
+ * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
+ */
+ private long earliestRunTimeElapsedMillis;
+ /**
+ * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
+ * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
+ */
+ private long latestRunTimeElapsedMillis;
+ /** How many times this job has failed, used to compute back-off. */
+ private final int numFailures;
+
+ /** Provide a handle to the service that this job will be run on. */
+ public int getServiceToken() {
+ return uId;
+ }
+
+ private JobStatus(JobInfo job, int uId, boolean persisted, int numFailures) {
+ this.job = job;
+ this.uId = uId;
+ this.numFailures = numFailures;
+ this.persisted = persisted;
+ }
+
+ /** Create a newly scheduled job. */
+ public JobStatus(JobInfo job, int uId, boolean persisted) {
+ this(job, uId, persisted, 0);
+
+ final long elapsedNow = SystemClock.elapsedRealtime();
+
+ if (job.isPeriodic()) {
+ earliestRunTimeElapsedMillis = elapsedNow;
+ latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
+ } else {
+ earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
+ elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
+ latestRunTimeElapsedMillis = job.hasLateConstraint() ?
+ elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
+ }
+ }
+
+ /**
+ * Create a new JobStatus that was loaded from disk. We ignore the provided
+ * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job
+ * from the {@link com.android.server.job.JobStore} and still want to respect its
+ * wallclock runtime rather than resetting it on every boot.
+ * We consider a freshly loaded job to no longer be in back-off.
+ */
+ public JobStatus(JobInfo job, int uId, long earliestRunTimeElapsedMillis,
+ long latestRunTimeElapsedMillis) {
+ this(job, uId, true, 0);
+
+ this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
+ this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
+ }
+
+ /** Create a new job to be rescheduled with the provided parameters. */
+ public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis,
+ long newLatestRuntimeElapsedMillis, int backoffAttempt) {
+ this(rescheduling.job, rescheduling.getUid(), rescheduling.isPersisted(), backoffAttempt);
+
+ earliestRunTimeElapsedMillis = newEarliestRuntimeElapsedMillis;
+ latestRunTimeElapsedMillis = newLatestRuntimeElapsedMillis;
+ }
+
+ public JobInfo getJob() {
+ return job;
+ }
+
+ public int getJobId() {
+ return job.getId();
+ }
+
+ public int getNumFailures() {
+ return numFailures;
+ }
+
+ public ComponentName getServiceComponent() {
+ return job.getService();
+ }
+
+ public int getUserId() {
+ return UserHandle.getUserId(uId);
+ }
+
+ public int getUid() {
+ return uId;
+ }
+
+ public PersistableBundle getExtras() {
+ return job.getExtras();
+ }
+
+ public boolean hasConnectivityConstraint() {
+ return job.getNetworkCapabilities() == JobInfo.NetworkType.ANY;
+ }
+
+ public boolean hasUnmeteredConstraint() {
+ return job.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED;
+ }
+
+ public boolean hasChargingConstraint() {
+ return job.isRequireCharging();
+ }
+
+ public boolean hasTimingDelayConstraint() {
+ return earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME;
+ }
+
+ public boolean hasDeadlineConstraint() {
+ return latestRunTimeElapsedMillis != NO_LATEST_RUNTIME;
+ }
+
+ public boolean hasIdleConstraint() {
+ return job.isRequireDeviceIdle();
+ }
+
+ public long getEarliestRunTime() {
+ return earliestRunTimeElapsedMillis;
+ }
+
+ public long getLatestRunTimeElapsed() {
+ return latestRunTimeElapsedMillis;
+ }
+
+ public boolean isPersisted() {
+ return persisted;
+ }
+ /**
+ * @return Whether or not this job is ready to run, based on its requirements.
+ */
+ public synchronized boolean isReady() {
+ return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
+ && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
+ && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
+ && (!hasUnmeteredConstraint() || unmeteredConstraintSatisfied.get())
+ && (!hasIdleConstraint() || idleConstraintSatisfied.get())
+ // Also ready if the deadline has expired - special case.
+ || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get());
+ }
+
+ public boolean matches(int uid, int jobId) {
+ return this.job.getId() == jobId && this.uId == uid;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(hashCode()).substring(0, 3) + ".."
+ + ":[" + job.getService().getPackageName() + ",jId=" + job.getId()
+ + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
+ + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
+ + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
+ + (isReady() ? "(READY)" : "")
+ + "]";
+ }
+ // Dumpsys infrastructure
+ public void dump(PrintWriter pw, String prefix) {
+ pw.println(this.toString());
+ }
+}
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
new file mode 100644
index 0000000..81658bf
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers;
+
+import android.content.Context;
+
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateChangedListener;
+
+import java.io.PrintWriter;
+
+/**
+ * Incorporates shared controller logic between the various controllers of the JobManager.
+ * These are solely responsible for tracking a list of jobs, and notifying the JM when these
+ * are ready to run, or whether they must be stopped.
+ */
+public abstract class StateController {
+ protected static final boolean DEBUG = true;
+ protected Context mContext;
+ protected StateChangedListener mStateChangedListener;
+
+ public StateController(StateChangedListener stateChangedListener, Context context) {
+ mStateChangedListener = stateChangedListener;
+ mContext = context;
+ }
+
+ /**
+ * Implement the logic here to decide whether a job should be tracked by this controller.
+ * This logic is put here so the JobManger can be completely agnostic of Controller logic.
+ * Also called when updating a task, so implementing controllers have to be aware of
+ * preexisting tasks.
+ */
+ public abstract void maybeStartTrackingJob(JobStatus jobStatus);
+ /**
+ * Remove task - this will happen if the task is cancelled, completed, etc.
+ */
+ public abstract void maybeStopTrackingJob(JobStatus jobStatus);
+
+ public abstract void dumpControllerState(PrintWriter pw);
+
+}
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
new file mode 100644
index 0000000..e46226c
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.StateChangedListener;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * This class sets an alarm for the next expiring job, and determines whether a job's minimum
+ * delay has been satisfied.
+ */
+public class TimeController extends StateController {
+ private static final String TAG = "JobScheduler.Time";
+ private static final String ACTION_JOB_EXPIRED =
+ "android.content.jobscheduler.JOB_DEADLINE_EXPIRED";
+ private static final String ACTION_JOB_DELAY_EXPIRED =
+ "android.content.jobscheduler.JOB_DELAY_EXPIRED";
+
+ /** Set an alarm for the next job expiry. */
+ private final PendingIntent mDeadlineExpiredAlarmIntent;
+ /** Set an alarm for the next job delay expiry. This*/
+ private final PendingIntent mNextDelayExpiredAlarmIntent;
+ /** Constant time determining how near in the future we'll set an alarm for. */
+ private static final long MIN_WAKEUP_INTERVAL_MILLIS = 15 * 1000;
+
+ private long mNextJobExpiredElapsedMillis;
+ private long mNextDelayExpiredElapsedMillis;
+
+ private AlarmManager mAlarmService = null;
+ /** List of tracked jobs, sorted asc. by deadline */
+ private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>();
+ /** Singleton. */
+ private static TimeController mSingleton;
+
+ public static synchronized TimeController get(JobSchedulerService jms) {
+ if (mSingleton == null) {
+ mSingleton = new TimeController(jms, jms.getContext());
+ }
+ return mSingleton;
+ }
+
+ private TimeController(StateChangedListener stateChangedListener, Context context) {
+ super(stateChangedListener, context);
+ mDeadlineExpiredAlarmIntent =
+ PendingIntent.getBroadcast(mContext, 0 /* ignored */,
+ new Intent(ACTION_JOB_EXPIRED), 0);
+ mNextDelayExpiredAlarmIntent =
+ PendingIntent.getBroadcast(mContext, 0 /* ignored */,
+ new Intent(ACTION_JOB_DELAY_EXPIRED), 0);
+ mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
+ mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
+
+ // Register BR for these intents.
+ IntentFilter intentFilter = new IntentFilter(ACTION_JOB_EXPIRED);
+ intentFilter.addAction(ACTION_JOB_DELAY_EXPIRED);
+ mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter);
+ }
+
+ /**
+ * Check if the job has a timing constraint, and if so determine where to insert it in our
+ * list.
+ */
+ @Override
+ public synchronized void maybeStartTrackingJob(JobStatus job) {
+ if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
+ maybeStopTrackingJob(job);
+ ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
+ while (it.hasPrevious()) {
+ JobStatus ts = it.previous();
+ if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) {
+ // Insert
+ break;
+ }
+ }
+ it.add(job);
+ maybeUpdateAlarms(
+ job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
+ job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE);
+ }
+ }
+
+ /**
+ * When we stop tracking a job, we only need to update our alarms if the job we're no longer
+ * tracking was the one our alarms were based off of.
+ * Really an == comparison should be enough, but why play with fate? We'll do <=.
+ */
+ @Override
+ public synchronized void maybeStopTrackingJob(JobStatus job) {
+ if (mTrackedJobs.remove(job)) {
+ checkExpiredDelaysAndResetAlarm();
+ checkExpiredDeadlinesAndResetAlarm();
+ }
+ }
+
+ /**
+ * Determines whether this controller can stop tracking the given job.
+ * The controller is no longer interested in a job once its time constraint is satisfied, and
+ * the job's deadline is fulfilled - unlike other controllers a time constraint can't toggle
+ * back and forth.
+ */
+ private boolean canStopTrackingJob(JobStatus job) {
+ return (!job.hasTimingDelayConstraint() ||
+ job.timeDelayConstraintSatisfied.get()) &&
+ (!job.hasDeadlineConstraint() ||
+ job.deadlineConstraintSatisfied.get());
+ }
+
+ private void ensureAlarmService() {
+ if (mAlarmService == null) {
+ mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ }
+ }
+
+ /**
+ * Checks list of jobs for ones that have an expired deadline, sending them to the JobScheduler
+ * if so, removing them from this list, and updating the alarm for the next expiry time.
+ */
+ private synchronized void checkExpiredDeadlinesAndResetAlarm() {
+ long nextExpiryTime = Long.MAX_VALUE;
+ final long nowElapsedMillis = SystemClock.elapsedRealtime();
+
+ Iterator<JobStatus> it = mTrackedJobs.iterator();
+ while (it.hasNext()) {
+ JobStatus job = it.next();
+ if (!job.hasDeadlineConstraint()) {
+ continue;
+ }
+ final long jobDeadline = job.getLatestRunTimeElapsed();
+
+ if (jobDeadline <= nowElapsedMillis) {
+ job.deadlineConstraintSatisfied.set(true);
+ mStateChangedListener.onRunJobNow(job);
+ it.remove();
+ } else { // Sorted by expiry time, so take the next one and stop.
+ nextExpiryTime = jobDeadline;
+ break;
+ }
+ }
+ setDeadlineExpiredAlarm(nextExpiryTime);
+ }
+
+ /**
+ * Handles alarm that notifies us that a job's delay has expired. Iterates through the list of
+ * tracked jobs and marks them as ready as appropriate.
+ */
+ private synchronized void checkExpiredDelaysAndResetAlarm() {
+ final long nowElapsedMillis = SystemClock.elapsedRealtime();
+ long nextDelayTime = Long.MAX_VALUE;
+ boolean ready = false;
+ Iterator<JobStatus> it = mTrackedJobs.iterator();
+ while (it.hasNext()) {
+ final JobStatus job = it.next();
+ if (!job.hasTimingDelayConstraint()) {
+ continue;
+ }
+ final long jobDelayTime = job.getEarliestRunTime();
+ if (jobDelayTime <= nowElapsedMillis) {
+ job.timeDelayConstraintSatisfied.set(true);
+ if (canStopTrackingJob(job)) {
+ it.remove();
+ }
+ if (job.isReady()) {
+ ready = true;
+ }
+ } else { // Keep going through list to get next delay time.
+ if (nextDelayTime > jobDelayTime) {
+ nextDelayTime = jobDelayTime;
+ }
+ }
+ }
+ if (ready) {
+ mStateChangedListener.onControllerStateChanged();
+ }
+ setDelayExpiredAlarm(nextDelayTime);
+ }
+
+ private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
+ if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
+ setDelayExpiredAlarm(delayExpiredElapsed);
+ }
+ if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) {
+ setDeadlineExpiredAlarm(deadlineExpiredElapsed);
+ }
+ }
+
+ /**
+ * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's
+ * delay will expire.
+ * This alarm <b>will not</b> wake up the phone.
+ */
+ private void setDelayExpiredAlarm(long alarmTimeElapsedMillis) {
+ final long earliestWakeupTimeElapsed =
+ SystemClock.elapsedRealtime() + MIN_WAKEUP_INTERVAL_MILLIS;
+ if (alarmTimeElapsedMillis < earliestWakeupTimeElapsed) {
+ alarmTimeElapsedMillis = earliestWakeupTimeElapsed;
+ }
+ mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
+ updateAlarmWithPendingIntent(mNextDelayExpiredAlarmIntent, mNextDelayExpiredElapsedMillis);
+ }
+
+ /**
+ * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's
+ * deadline will expire.
+ * This alarm <b>will</b> wake up the phone.
+ */
+ private void setDeadlineExpiredAlarm(long alarmTimeElapsedMillis) {
+ final long earliestWakeupTimeElapsed =
+ SystemClock.elapsedRealtime() + MIN_WAKEUP_INTERVAL_MILLIS;
+ if (alarmTimeElapsedMillis < earliestWakeupTimeElapsed) {
+ alarmTimeElapsedMillis = earliestWakeupTimeElapsed;
+ }
+ mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
+ updateAlarmWithPendingIntent(mDeadlineExpiredAlarmIntent, mNextJobExpiredElapsedMillis);
+ }
+
+ private void updateAlarmWithPendingIntent(PendingIntent pi, long alarmTimeElapsed) {
+ ensureAlarmService();
+ if (alarmTimeElapsed == Long.MAX_VALUE) {
+ mAlarmService.cancel(pi);
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Setting " + pi.getIntent().getAction() + " for: " + alarmTimeElapsed);
+ }
+ mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsed, pi);
+ }
+ }
+
+ private final BroadcastReceiver mAlarmExpiredReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "Just received alarm: " + intent.getAction());
+ }
+ // A job has just expired, so we run through the list of jobs that we have and
+ // notify our StateChangedListener.
+ if (ACTION_JOB_EXPIRED.equals(intent.getAction())) {
+ checkExpiredDeadlinesAndResetAlarm();
+ } else if (ACTION_JOB_DELAY_EXPIRED.equals(intent.getAction())) {
+ checkExpiredDelaysAndResetAlarm();
+ }
+ }
+ };
+
+ @Override
+ public void dumpControllerState(PrintWriter pw) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ pw.println("Alarms (" + SystemClock.elapsedRealtime() + ")");
+ pw.println(
+ "Next delay alarm in " + (mNextDelayExpiredElapsedMillis - nowElapsed)/1000 + "s");
+ pw.println("Next deadline alarm in " + (mNextJobExpiredElapsedMillis - nowElapsed)/1000
+ + "s");
+ pw.println("Tracking:");
+ for (JobStatus ts : mTrackedJobs) {
+ pw.println(String.valueOf(ts.hashCode()).substring(0, 3) + ".."
+ + ": (" + (ts.hasTimingDelayConstraint() ? ts.getEarliestRunTime() : "N/A")
+ + ", " + (ts.hasDeadlineConstraint() ?ts.getLatestRunTimeElapsed() : "N/A")
+ + ")");
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 51ee93b..2699fea 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -96,7 +96,7 @@
Looper.myLooper());
}
- public boolean isSupported() {
+ public static boolean isSupported() {
return nativeIsSupported();
}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index c6cf68f..c5b6c7b 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -83,6 +83,8 @@
import java.util.Date;
import java.util.Map.Entry;
import java.util.Properties;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
/**
* A GPS implementation of LocationProvider used by LocationManager.
@@ -158,6 +160,12 @@
private static final int AGPS_TYPE_SUPL = 1;
private static final int AGPS_TYPE_C2K = 2;
+ // these must match the definitions in gps.h
+ private static final int APN_INVALID = 0;
+ private static final int APN_IPV4 = 1;
+ private static final int APN_IPV6 = 2;
+ private static final int APN_IPV4V6 = 3;
+
// for mAGpsDataConnectionState
private static final int AGPS_DATA_CONNECTION_CLOSED = 0;
private static final int AGPS_DATA_CONNECTION_OPENING = 1;
@@ -312,8 +320,9 @@
private Handler mHandler;
private String mAGpsApn;
+ private int mApnIpType;
private int mAGpsDataConnectionState;
- private int mAGpsDataConnectionIpAddr;
+ private InetAddress mAGpsDataConnectionIpAddr;
private final ConnectivityManager mConnMgr;
private final GpsNetInitiatedHandler mNIHandler;
@@ -595,28 +604,28 @@
if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL
&& mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
- String apnName = info.getExtraInfo();
if (mNetworkAvailable) {
+ String apnName = info.getExtraInfo();
if (apnName == null) {
/* Assign a dummy value in the case of C2K as otherwise we will have a runtime
exception in the following call to native_agps_data_conn_open*/
apnName = "dummy-apn";
}
mAGpsApn = apnName;
- if (DEBUG) Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr);
- if (mAGpsDataConnectionIpAddr != 0xffffffff) {
- boolean route_result;
- if (DEBUG) Log.d(TAG, "call requestRouteToHost");
- route_result = mConnMgr.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_SUPL,
- mAGpsDataConnectionIpAddr);
- if (route_result == false) Log.d(TAG, "call requestRouteToHost failed");
+ mApnIpType = getApnIpType(apnName);
+ setRouting();
+ if (DEBUG) {
+ String message = String.format(
+ "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s",
+ mAGpsApn, mApnIpType);
+ Log.d(TAG, message);
}
- if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open");
- native_agps_data_conn_open(apnName);
+ native_agps_data_conn_open(mAGpsApn, mApnIpType);
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
} else {
- if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed");
+ Log.e(TAG, "call native_agps_data_conn_failed, info: " + info);
mAGpsApn = null;
+ mApnIpType = APN_INVALID;
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
native_agps_data_conn_failed();
}
@@ -1324,7 +1333,7 @@
/**
* called from native code to update AGPS status
*/
- private void reportAGpsStatus(int type, int status, int ipaddr) {
+ private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
switch (status) {
case GPS_REQUEST_AGPS_DATA_CONN:
if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
@@ -1333,20 +1342,20 @@
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
int result = mConnMgr.startUsingNetworkFeature(
ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
- mAGpsDataConnectionIpAddr = ipaddr;
+ if (ipaddr != null) {
+ try {
+ mAGpsDataConnectionIpAddr = InetAddress.getByAddress(ipaddr);
+ } catch (UnknownHostException e) {
+ Log.e(TAG, "Bad IP Address: " + ipaddr, e);
+ mAGpsDataConnectionIpAddr = null;
+ }
+ }
+
if (result == PhoneConstants.APN_ALREADY_ACTIVE) {
if (DEBUG) Log.d(TAG, "PhoneConstants.APN_ALREADY_ACTIVE");
if (mAGpsApn != null) {
- Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr);
- if (mAGpsDataConnectionIpAddr != 0xffffffff) {
- boolean route_result;
- if (DEBUG) Log.d(TAG, "call requestRouteToHost");
- route_result = mConnMgr.requestRouteToHost(
- ConnectivityManager.TYPE_MOBILE_SUPL,
- mAGpsDataConnectionIpAddr);
- if (route_result == false) Log.d(TAG, "call requestRouteToHost failed");
- }
- native_agps_data_conn_open(mAGpsApn);
+ setRouting();
+ native_agps_data_conn_open(mAGpsApn, mApnIpType);
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN;
} else {
Log.e(TAG, "mAGpsApn not set when receiving PhoneConstants.APN_ALREADY_ACTIVE");
@@ -1370,6 +1379,7 @@
ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
native_agps_data_conn_closed();
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED;
+ mAGpsDataConnectionIpAddr = null;
}
break;
case GPS_AGPS_DATA_CONNECTED:
@@ -1821,21 +1831,97 @@
private String getSelectedApn() {
Uri uri = Uri.parse("content://telephony/carriers/preferapn");
- String apn = null;
-
- Cursor cursor = mContext.getContentResolver().query(uri, new String[] {"apn"},
- null, null, Carriers.DEFAULT_SORT_ORDER);
-
- if (null != cursor) {
- try {
- if (cursor.moveToFirst()) {
- apn = cursor.getString(0);
- }
- } finally {
+ Cursor cursor = null;
+ try {
+ cursor = mContext.getContentResolver().query(
+ uri,
+ new String[] { "apn" },
+ null /* selection */,
+ null /* selectionArgs */,
+ Carriers.DEFAULT_SORT_ORDER);
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getString(0);
+ } else {
+ Log.e(TAG, "No APN found to select.");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error encountered on selectiong the APN.", e);
+ } finally {
+ if (cursor != null) {
cursor.close();
}
}
- return apn;
+
+ return null;
+ }
+
+ private int getApnIpType(String apn) {
+ if (apn == null) {
+ return APN_INVALID;
+ }
+
+ // look for cached data to use
+ if (apn.equals(mAGpsApn) && mApnIpType != APN_INVALID) {
+ return mApnIpType;
+ }
+
+ String selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn);
+ Cursor cursor = null;
+ try {
+ cursor = mContext.getContentResolver().query(
+ Carriers.CONTENT_URI,
+ new String[] { Carriers.PROTOCOL },
+ selection,
+ null,
+ Carriers.DEFAULT_SORT_ORDER);
+
+ if (null != cursor && cursor.moveToFirst()) {
+ return translateToApnIpType(cursor.getString(0), apn);
+ } else {
+ Log.e(TAG, "No entry found in query for APN: " + apn);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error encountered on APN query for: " + apn, e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return APN_INVALID;
+ }
+
+ private int translateToApnIpType(String ipProtocol, String apn) {
+ if ("IP".equals(ipProtocol)) {
+ return APN_IPV4;
+ }
+ if ("IPV6".equals(ipProtocol)) {
+ return APN_IPV6;
+ }
+ if ("IPV4V6".equals(ipProtocol)) {
+ return APN_IPV4V6;
+ }
+
+ // we hit the default case so the ipProtocol is not recognized
+ String message = String.format("Unknown IP Protocol: %s, for APN: %s", ipProtocol, apn);
+ Log.e(TAG, message);
+ return APN_INVALID;
+ }
+
+ private void setRouting() {
+ if (mAGpsDataConnectionIpAddr == null) {
+ return;
+ }
+
+ boolean result = mConnMgr.requestRouteToHostAddress(
+ ConnectivityManager.TYPE_MOBILE_SUPL,
+ mAGpsDataConnectionIpAddr);
+
+ if (!result) {
+ Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr);
+ } else if (DEBUG) {
+ Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr);
+ }
}
@Override
@@ -1897,7 +1983,7 @@
private native String native_get_internal_state();
// AGPS Support
- private native void native_agps_data_conn_open(String apn);
+ private native void native_agps_data_conn_open(String apn, int apnIpType);
private native void native_agps_data_conn_closed();
private native void native_agps_data_conn_failed();
private native void native_agps_ni_message(byte [] msg, int length);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 737ffda..9ae8aed 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -130,7 +130,8 @@
mOwnerPid = ownerPid;
mOwnerUid = ownerUid;
mUserId = userId;
- mSessionInfo = new MediaSessionInfo(UUID.randomUUID().toString(), ownerPackageName);
+ mSessionInfo = new MediaSessionInfo(UUID.randomUUID().toString(), ownerPackageName,
+ ownerPid);
mTag = tag;
mController = new ControllerStub();
mSession = new SessionStub();
@@ -943,6 +944,16 @@
}
@Override
+ public MediaSessionInfo getSessionInfo() {
+ return mSessionInfo;
+ }
+
+ @Override
+ public long getFlags() {
+ return mFlags;
+ }
+
+ @Override
public void play() throws RemoteException {
mSessionCb.play();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 87665e1..67065ba 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -29,9 +29,11 @@
import android.media.AudioManager;
import android.media.IAudioService;
import android.media.routeprovider.RouteRequest;
+import android.media.session.IActiveSessionsListener;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.ISessionManager;
+import android.media.session.MediaSessionToken;
import android.media.session.RouteInfo;
import android.media.session.RouteOptions;
import android.media.session.MediaSession;
@@ -39,6 +41,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -75,10 +78,12 @@
private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
+ private final ArrayList<SessionsListenerRecord> mSessionsListeners
+ = new ArrayList<SessionsListenerRecord>();
// private final ArrayList<MediaRouteProviderProxy> mProviders
// = new ArrayList<MediaRouteProviderProxy>();
private final Object mLock = new Object();
- private final Handler mHandler = new Handler();
+ private final MessageHandler mHandler = new MessageHandler();
private final PowerManager.WakeLock mMediaEventWakeLock;
private KeyguardManager mKeyguardManager;
@@ -200,15 +205,20 @@
}
}
}
+ mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
}
public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
+ boolean updateSessions = false;
synchronized (mLock) {
if (!mAllSessions.contains(record)) {
Log.d(TAG, "Unknown session changed playback state. Ignoring.");
return;
}
- mPriorityStack.onPlaystateChange(record, oldState, newState);
+ updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
+ }
+ if (updateSessions) {
+ mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
}
}
@@ -315,6 +325,8 @@
// ignore exceptions while destroying a session.
}
session.onDestroy();
+
+ mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
}
private void enforcePackageName(String packageName, int uid) {
@@ -428,6 +440,8 @@
UserRecord user = getOrCreateUser(userId);
user.addSessionLocked(session);
+ mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
+
if (DEBUG) {
Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
}
@@ -453,11 +467,43 @@
return -1;
}
+ private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
+ for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+ if (mSessionsListeners.get(i).mListener == listener) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
private boolean isSessionDiscoverable(MediaSessionRecord record) {
// TODO probably want to check more than if it's active.
return record.isActive();
}
+ private void pushSessionsChanged(int userId) {
+ synchronized (mLock) {
+ List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
+ int size = records.size();
+ ArrayList<MediaSessionToken> tokens = new ArrayList<MediaSessionToken>();
+ for (int i = 0; i < size; i++) {
+ tokens.add(new MediaSessionToken(records.get(i).getControllerBinder()));
+ }
+ for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
+ SessionsListenerRecord record = mSessionsListeners.get(i);
+ if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
+ try {
+ record.mListener.onActiveSessionsChanged(tokens);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
+ e);
+ mSessionsListeners.remove(i);
+ }
+ }
+ }
+ }
+ }
+
private MediaRouteProviderProxy.RoutesListener mRoutesCallback
= new MediaRouteProviderProxy.RoutesListener() {
@Override
@@ -613,6 +659,23 @@
};
}
+ final class SessionsListenerRecord implements IBinder.DeathRecipient {
+ private final IActiveSessionsListener mListener;
+ private final int mUserId;
+
+ public SessionsListenerRecord(IActiveSessionsListener listener, int userId) {
+ mListener = listener;
+ mUserId = userId;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ mSessionsListeners.remove(this);
+ }
+ }
+ }
+
class SessionManagerImpl extends ISessionManager.Stub {
private static final String EXTRA_WAKELOCK_ACQUIRED =
"android.media.AudioService.WAKELOCK_ACQUIRED";
@@ -648,20 +711,7 @@
final long token = Binder.clearCallingIdentity();
try {
- String packageName = null;
- if (componentName != null) {
- // If they gave us a component name verify they own the
- // package
- packageName = componentName.getPackageName();
- enforcePackageName(packageName, uid);
- }
- // Check that they can make calls on behalf of the user and
- // get the final user id
- int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
- true /* allowAll */, true /* requireFull */, "getSessions", packageName);
- // Check if they have the permissions or their component is
- // enabled for the user they're calling from.
- enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
+ int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
ArrayList<IBinder> binders = new ArrayList<IBinder>();
synchronized (mLock) {
ArrayList<MediaSessionRecord> records = mPriorityStack
@@ -677,6 +727,52 @@
}
}
+ @Override
+ public void addSessionsListener(IActiveSessionsListener listener,
+ ComponentName componentName, int userId) throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+
+ try {
+ int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
+ synchronized (mLock) {
+ int index = findIndexOfSessionsListenerLocked(listener);
+ if (index != -1) {
+ Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
+ return;
+ }
+ SessionsListenerRecord record = new SessionsListenerRecord(listener,
+ resolvedUserId);
+ try {
+ listener.asBinder().linkToDeath(record, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
+ return;
+ }
+ mSessionsListeners.add(record);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeSessionsListener(IActiveSessionsListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ int index = findIndexOfSessionsListenerLocked(listener);
+ if (index != -1) {
+ SessionsListenerRecord record = mSessionsListeners.remove(index);
+ try {
+ record.mListener.asBinder().unlinkToDeath(record, 0);
+ } catch (Exception e) {
+ // ignore exceptions, the record is being removed
+ }
+ }
+ }
+ }
+
/**
* Handles the dispatching of the media button events to one of the
* registered listeners, or if there was none, broadcast an
@@ -764,6 +860,25 @@
}
}
+ private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
+ final int uid) {
+ String packageName = null;
+ if (componentName != null) {
+ // If they gave us a component name verify they own the
+ // package
+ packageName = componentName.getPackageName();
+ enforcePackageName(packageName, uid);
+ }
+ // Check that they can make calls on behalf of the user and
+ // get the final user id
+ int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+ true /* allowAll */, true /* requireFull */, "getSessions", packageName);
+ // Check if they have the permissions or their component is
+ // enabled for the user they're calling from.
+ enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
+ return resolvedUserId;
+ }
+
private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
MediaSessionRecord session) {
int direction = 0;
@@ -781,25 +896,36 @@
}
if (session == null) {
- for (int i = 0; i < steps; i++) {
- try {
- mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
- flags, getContext().getOpPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error adjusting default volume.", e);
+ try {
+ if (delta == 0) {
+ mAudioService.adjustSuggestedStreamVolume(delta, suggestedStream, flags,
+ getContext().getOpPackageName());
+ } else {
+ for (int i = 0; i < steps; i++) {
+ mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
+ flags, getContext().getOpPackageName());
+ }
}
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error adjusting default volume.", e);
}
} else {
if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
- for (int i = 0; i < steps; i++) {
- try {
- mAudioService.adjustSuggestedStreamVolume(direction,
+ try {
+ if (delta == 0) {
+ mAudioService.adjustSuggestedStreamVolume(delta,
session.getAudioStream(), flags,
getContext().getOpPackageName());
- } catch (RemoteException e) {
- Log.e(TAG, "Error adjusting volume for stream "
- + session.getAudioStream(), e);
+ } else {
+ for (int i = 0; i < steps; i++) {
+ mAudioService.adjustSuggestedStreamVolume(direction,
+ session.getAudioStream(), flags,
+ getContext().getOpPackageName());
+ }
}
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error adjusting volume for stream "
+ + session.getAudioStream(), e);
}
} else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) {
session.adjustVolumeBy(delta);
@@ -994,4 +1120,20 @@
};
}
+ final class MessageHandler extends Handler {
+ private static final int MSG_SESSIONS_CHANGED = 1;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SESSIONS_CHANGED:
+ pushSessionsChanged(msg.arg1);
+ break;
+ }
+ }
+
+ public void post(int what, int arg1, int arg2) {
+ obtainMessage(what, arg1, arg2).sendToTarget();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 803dee2..144ccfa 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -88,16 +88,19 @@
* @param record The record that changed.
* @param oldState Its old playback state.
* @param newState Its new playback state.
+ * @return true if the priority order was updated, false otherwise.
*/
- public void onPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
+ public boolean onPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
if (shouldUpdatePriority(oldState, newState)) {
mSessions.remove(record);
mSessions.add(0, record);
clearCache();
+ return true;
} else if (newState == PlaybackState.STATE_PAUSED) {
// Just clear the volume cache in this case
mCachedVolumeDefault = null;
}
+ return false;
}
/**
diff --git a/services/core/java/com/android/server/net/IpConfigStore.java b/services/core/java/com/android/server/net/IpConfigStore.java
index aee7679..907eeb2 100644
--- a/services/core/java/com/android/server/net/IpConfigStore.java
+++ b/services/core/java/com/android/server/net/IpConfigStore.java
@@ -81,15 +81,15 @@
for (LinkAddress linkAddr : linkProperties.getLinkAddresses()) {
out.writeUTF(LINK_ADDRESS_KEY);
out.writeUTF(linkAddr.getAddress().getHostAddress());
- out.writeInt(linkAddr.getNetworkPrefixLength());
+ out.writeInt(linkAddr.getPrefixLength());
}
for (RouteInfo route : linkProperties.getRoutes()) {
out.writeUTF(GATEWAY_KEY);
- LinkAddress dest = route.getDestination();
+ LinkAddress dest = route.getDestinationLinkAddress();
if (dest != null) {
out.writeInt(1);
out.writeUTF(dest.getAddress().getHostAddress());
- out.writeInt(dest.getNetworkPrefixLength());
+ out.writeInt(dest.getPrefixLength());
} else {
out.writeInt(0);
}
@@ -100,7 +100,7 @@
out.writeInt(0);
}
}
- for (InetAddress inetAddr : linkProperties.getDnses()) {
+ for (InetAddress inetAddr : linkProperties.getDnsServers()) {
out.writeUTF(DNS_KEY);
out.writeUTF(inetAddr.getHostAddress());
}
@@ -232,7 +232,7 @@
}
linkProperties.addRoute(new RouteInfo(dest, gateway));
} else if (key.equals(DNS_KEY)) {
- linkProperties.addDns(
+ linkProperties.addDnsServer(
NetworkUtils.numericToInetAddress(in.readUTF()));
} else if (key.equals(PROXY_SETTINGS_KEY)) {
proxySettings = ProxySettings.valueOf(in.readUTF());
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index dbfb1cf..15f0ebf 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,18 +16,14 @@
package com.android.server.notification;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.service.notification.Condition;
@@ -35,7 +31,6 @@
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
-import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -45,7 +40,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Date;
+import java.util.Objects;
public class ConditionProviders extends ManagedServices {
private static final Condition[] NO_CONDITIONS = new Condition[0];
@@ -54,7 +49,9 @@
private final ArrayMap<IBinder, IConditionListener> mListeners
= new ArrayMap<IBinder, IConditionListener>();
private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
- private final CountdownConditionHelper mCountdownHelper = new CountdownConditionHelper();
+ private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
+
+ private Uri mExitConditionId;
public ConditionProviders(Context context, Handler handler,
UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
@@ -86,10 +83,13 @@
}
pw.print(" mRecords("); pw.print(mRecords.size()); pw.println("):");
for (int i = 0; i < mRecords.size(); i++) {
- pw.print(" "); pw.println(mRecords.get(i));
+ final ConditionRecord r = mRecords.get(i);
+ pw.print(" "); pw.println(r);
+ final String countdownDesc = CountdownConditionProvider.tryParseDescription(r.id);
+ if (countdownDesc != null) {
+ pw.print(" ("); pw.print(countdownDesc); pw.println(")");
+ }
}
- pw.print(" mCountdownHelper: ");
- pw.println(mCountdownHelper.getCurrentConditionDescription());
}
}
@@ -99,6 +99,14 @@
}
@Override
+ public void onBootPhaseAppsCanStart() {
+ super.onBootPhaseAppsCanStart();
+ mCountdown.attachBase(mContext);
+ registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
+ UserHandle.USER_OWNER);
+ }
+
+ @Override
protected void onServiceAdded(ManagedServiceInfo info) {
Slog.d(TAG, "onServiceAdded " + info);
final IConditionProvider provider = provider(info);
@@ -262,6 +270,15 @@
public void setZenModeCondition(Uri conditionId) {
if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
synchronized(mMutex) {
+ if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
+ // constructed by the client, make sure the record exists...
+ final ConditionRecord r = getRecordLocked(conditionId,
+ CountdownConditionProvider.COMPONENT);
+ if (r.info == null) {
+ // ... and is associated with the in-process service
+ r.info = checkServiceTokenLocked(mCountdown.asInterface());
+ }
+ }
final int N = mRecords.size();
for (int i = 0; i < N; i++) {
final ConditionRecord r = mRecords.get(i);
@@ -276,8 +293,11 @@
r.isManual = true;
}
}
+ if (!Objects.equals(mExitConditionId, conditionId)) {
+ mExitConditionId = conditionId;
+ saveZenConfigLocked();
+ }
}
- mCountdownHelper.setZenModeCondition(conditionId);
}
private void subscribeLocked(ConditionRecord r) {
@@ -395,6 +415,7 @@
return;
}
synchronized (mMutex) {
+ mExitConditionId = config.exitConditionId;
if (config.conditionComponents == null || config.conditionIds == null
|| config.conditionComponents.length != config.conditionIds.length) {
if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
@@ -441,82 +462,11 @@
config.conditionIds[i] = r.id;
}
}
+ config.exitConditionId = mExitConditionId;
if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
mZenModeHelper.setConfig(config);
}
- private final class CountdownConditionHelper extends BroadcastReceiver {
- private static final String ACTION = "CountdownConditionHelper";
- private static final int REQUEST_CODE = 100;
- private static final String EXTRA_TIME = "time";
-
- private long mCurrent;
-
- public CountdownConditionHelper() {
- mContext.registerReceiver(this, new IntentFilter(ACTION));
- }
-
- public void setZenModeCondition(Uri conditionId) {
- final long time = tryParseCondition(conditionId);
- final AlarmManager alarms = (AlarmManager)
- mContext.getSystemService(Context.ALARM_SERVICE);
- final Intent intent = new Intent(ACTION).putExtra(EXTRA_TIME, time)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
- intent, PendingIntent.FLAG_UPDATE_CURRENT);
- alarms.cancel(pendingIntent);
- mCurrent = time;
- if (time > 0) {
- final long now = System.currentTimeMillis();
- final CharSequence span =
- DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
- Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future (%s), now=%s",
- ACTION, ts(time), time - now, span, ts(now)));
- alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
- }
- }
-
- public String getCurrentConditionDescription() {
- if (mCurrent == 0) return null;
- final long time = mCurrent;
- final long now = System.currentTimeMillis();
- final CharSequence span =
- DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
- return String.format("Scheduled for %s, %s in the future (%s), now=%s",
- ts(time), time - now, span, ts(now));
- }
-
- private String ts(long time) {
- return new Date(time) + " (" + time + ")";
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (ACTION.equals(intent.getAction())) {
- final long time = intent.getLongExtra(EXTRA_TIME, 0);
- Slog.d(TAG, "Countdown condition fired. time=" + time + " mCurrent=" + mCurrent);
- if (time > 0 && time == mCurrent) {
- // countdown condition is still the manual condition, leave zen
- mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
- ConditionProviders.this.setZenModeCondition(null);
- }
- }
- }
-
- private long tryParseCondition(Uri conditionId) {
- // condition://android/countdown/1399917958951
- if (!Condition.isValidId(conditionId, "android")) return 0;
- if (conditionId.getPathSegments().size() != 2
- || !"countdown".equals(conditionId.getPathSegments().get(0))) return 0;
- try {
- return Long.parseLong(conditionId.getPathSegments().get(1));
- } catch (RuntimeException e) {
- Slog.w(TAG, "Error parsing countdown condition: " + conditionId, e);
- return 0;
- }
- }
- }
-
private class ZenModeHelperCallback extends ZenModeHelper.Callback {
@Override
void onConfigChanged() {
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
new file mode 100644
index 0000000..0884f76
--- /dev/null
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -0,0 +1,146 @@
+/*
+ * 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.notification;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.IConditionProvider;
+import android.service.notification.ZenModeConfig;
+import android.text.format.DateUtils;
+import android.util.Slog;
+
+import java.util.Date;
+
+/** Built-in zen condition provider for simple time-based conditions */
+public class CountdownConditionProvider extends ConditionProviderService {
+ private static final String TAG = "CountdownConditionProvider";
+ private static final boolean DEBUG = false;
+
+ public static final ComponentName COMPONENT =
+ new ComponentName("android", CountdownConditionProvider.class.getName());
+
+ private static final String ACTION = CountdownConditionProvider.class.getName();
+ private static final int REQUEST_CODE = 100;
+ private static final String EXTRA_CONDITION_ID = "condition_id";
+
+ private final Context mContext = this;
+ private final Receiver mReceiver = new Receiver();
+
+ private boolean mConnected;
+
+ public CountdownConditionProvider() {
+ if (DEBUG) Slog.d(TAG, "new CountdownConditionProvider()");
+ }
+
+ @Override
+ public void onConnected() {
+ if (DEBUG) Slog.d(TAG, "onConnected");
+ mContext.registerReceiver(mReceiver, new IntentFilter(ACTION));
+ mConnected = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (DEBUG) Slog.d(TAG, "onDestroy");
+ if (mConnected) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mConnected = false;
+ }
+
+ @Override
+ public void onRequestConditions(int relevance) {
+ // by convention
+ }
+
+ @Override
+ public void onSubscribe(Uri conditionId) {
+ if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
+ final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
+ final AlarmManager alarms = (AlarmManager)
+ mContext.getSystemService(Context.ALARM_SERVICE);
+ final Intent intent = new Intent(ACTION).putExtra(EXTRA_CONDITION_ID, conditionId)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
+ intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ alarms.cancel(pendingIntent);
+ if (time > 0) {
+ final long now = System.currentTimeMillis();
+ final CharSequence span =
+ DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
+ if (DEBUG) Slog.d(TAG, String.format(
+ "Scheduling %s for %s, %s in the future (%s), now=%s",
+ ACTION, ts(time), time - now, span, ts(now)));
+ alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+ }
+ }
+
+ @Override
+ public void onUnsubscribe(Uri conditionId) {
+ // noop
+ }
+
+ private final class Receiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION.equals(intent.getAction())) {
+ final Uri conditionId = intent.getParcelableExtra(EXTRA_CONDITION_ID);
+ final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
+ if (DEBUG) Slog.d(TAG, "Countdown condition fired: " + conditionId);
+ if (time > 0) {
+ notifyCondition(newCondition(time, Condition.STATE_FALSE));
+ }
+ }
+ }
+ }
+
+ private static final Condition newCondition(long time, int state) {
+ return new Condition(ZenModeConfig.toCountdownConditionId(time),
+ "", "", "", 0, state,Condition.FLAG_RELEVANT_NOW);
+ }
+
+ public static String tryParseDescription(Uri conditionUri) {
+ final long time = ZenModeConfig.tryParseCountdownConditionId(conditionUri);
+ if (time == 0) return null;
+ final long now = System.currentTimeMillis();
+ final CharSequence span =
+ DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS);
+ return String.format("Scheduled for %s, %s in the future (%s), now=%s",
+ ts(time), time - now, span, ts(now));
+ }
+
+ private static String ts(long time) {
+ return new Date(time) + " (" + time + ")";
+ }
+
+ public void attachBase(Context base) {
+ attachBaseContext(base);
+ }
+
+ public IConditionProvider asInterface() {
+ return (IConditionProvider) onBind(null);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index b30baea..6cd4019 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -41,6 +41,6 @@
return -1 * Float.compare(leftPeple, rightPeople);
}
// then break ties by time, most recent first
- return -1 * Long.compare(lhs.sbn.getPostTime(), rhs.sbn.getPostTime());
+ return -1 * Long.compare(lhs.getRankingTimeMs(), rhs.getRankingTimeMs());
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cb78a45..06732ca 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -163,6 +163,7 @@
private long[] mDefaultVibrationPattern;
private long[] mFallbackVibrationPattern;
+ private boolean mUseAttentionLight;
boolean mSystemReady;
private boolean mDisableNotificationAlerts;
@@ -182,11 +183,13 @@
new ArrayMap<String, NotificationRecord>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
- ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
+ ArrayList<String> mLights = new ArrayList<String>();
NotificationRecord mLedNotification;
private AppOpsManager mAppOps;
+ private Archive mArchive;
+
// Notification control database. For now just contains disabled packages.
private AtomicFile mPolicyFile;
private HashSet<String> mBlockedPackages = new HashSet<String>();
@@ -222,10 +225,12 @@
private static final int REASON_LISTENER_CANCEL_ALL = 11;
private static class Archive {
- static final int BUFFER_SIZE = 250;
- ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
+ final int mBufferSize;
+ final ArrayDeque<StatusBarNotification> mBuffer;
- public Archive() {
+ public Archive(int size) {
+ mBufferSize = size;
+ mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize);
}
public String toString() {
@@ -239,7 +244,7 @@
}
public void record(StatusBarNotification nr) {
- if (mBuffer.size() == BUFFER_SIZE) {
+ if (mBuffer.size() == mBufferSize) {
mBuffer.removeFirst();
}
@@ -249,7 +254,6 @@
mBuffer.addLast(nr.cloneLight());
}
-
public void clear() {
mBuffer.clear();
}
@@ -299,7 +303,7 @@
}
public StatusBarNotification[] getArray(int count) {
- if (count == 0) count = Archive.BUFFER_SIZE;
+ if (count == 0) count = mBufferSize;
final StatusBarNotification[] a
= new StatusBarNotification[Math.min(count, mBuffer.size())];
Iterator<StatusBarNotification> iter = descendingIterator();
@@ -311,7 +315,7 @@
}
public StatusBarNotification[] getArray(int count, String pkg, int userId) {
- if (count == 0) count = Archive.BUFFER_SIZE;
+ if (count == 0) count = mBufferSize;
final StatusBarNotification[] a
= new StatusBarNotification[Math.min(count, mBuffer.size())];
Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId);
@@ -324,8 +328,6 @@
}
- Archive mArchive = new Archive();
-
private void loadPolicyFile() {
synchronized(mPolicyFile) {
mBlockedPackages.clear();
@@ -797,6 +799,8 @@
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
+ mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
+
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
// This works on the first boot because the setup wizard will toggle this
@@ -851,6 +855,9 @@
}
}
+ mArchive = new Archive(resources.getInteger(
+ R.integer.config_notificationServiceArchiveSize));
+
publishBinderService(Context.NOTIFICATION_SERVICE, mService);
publishLocalService(NotificationManagerInternal.class, mInternalService);
}
@@ -1238,7 +1245,7 @@
@Override
public ZenModeConfig getZenModeConfig() {
- checkCallerIsSystem();
+ enforceSystemOrSystemUI("INotificationManager.getZenModeConfig");
return mZenModeHelper.getConfig();
}
@@ -1478,14 +1485,19 @@
}
}
- // 1. initial score: buckets of 10, around the app
- int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
+ // 1. initial score: buckets of 10, around the app [-20..20]
+ final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
// 2. extract ranking signals from the notification data
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
user);
- NotificationRecord r = new NotificationRecord(n);
+ NotificationRecord r = new NotificationRecord(n, score);
+ NotificationRecord old = mNotificationsByKey.get(n.getKey());
+ if (old != null) {
+ // Retain ranking information from previous record
+ r.copyRankingInformation(old);
+ }
if (!mSignalExtractors.isEmpty()) {
for (NotificationSignalExtractor extractor : mSignalExtractors) {
try {
@@ -1502,27 +1514,18 @@
// blocked apps
if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
if (!isSystemNotification) {
- score = JUNK_SCORE;
+ r.score = JUNK_SCORE;
Slog.e(TAG, "Suppressing notification from package " + pkg
+ " by user request.");
}
}
- if (score < SCORE_DISPLAY_THRESHOLD) {
+ if (r.score < SCORE_DISPLAY_THRESHOLD) {
// Notification will be blocked because the score is too low.
return;
}
synchronized (mNotificationList) {
- applyZenModeLocked(r);
-
- // Should this notification make noise, vibe, or use the LED?
- final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) &&
- !r.isIntercepted();
- if (DBG || r.isIntercepted()) Slog.v(TAG,
- "pkg=" + pkg + " canInterrupt=" + canInterrupt +
- " intercept=" + r.isIntercepted());
- NotificationRecord old = null;
int index = indexOfNotificationLocked(n.getKey());
if (index < 0) {
mNotificationList.add(r);
@@ -1533,21 +1536,15 @@
mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
notification.flags |=
- old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
- // Retain ranking information from previous record
- r.copyRankingInformation(old);
+ old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
mNotificationsByKey.remove(old.sbn.getKey());
+ r.isUpdate = true;
}
mNotificationsByKey.put(n.getKey(), r);
- Collections.sort(mNotificationList, mRankingComparator);
+ applyZenModeLocked(r);
- // Ensure if this is a foreground service that the proper additional
- // flags are set.
- if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- notification.flags |= Notification.FLAG_ONGOING_EVENT
- | Notification.FLAG_NO_CLEAR;
- }
+ Collections.sort(mNotificationList, mRankingComparator);
final int currentUser;
final long token = Binder.clearCallingIdentity();
@@ -1569,20 +1566,11 @@
final long identity = Binder.clearCallingIdentity();
try {
mStatusBar.addNotification(n);
- if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
- && canInterrupt) {
- mAttentionLight.pulse();
- }
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- // Send accessibility events only for the current user.
- if (currentUser == userId) {
- sendAccessibilityEvent(notification, pkg);
- }
-
- mListeners.notifyPostedLocked(r.sbn);
+ mListeners.notifyPostedLocked(n);
} else {
Slog.e(TAG, "Not posting notification with icon==0: " + notification);
if (old != null && !old.isCanceled) {
@@ -1593,7 +1581,7 @@
Binder.restoreCallingIdentity(identity);
}
- mListeners.notifyRemovedLocked(r.sbn);
+ mListeners.notifyRemovedLocked(n);
}
// ATTENTION: in a future release we will bail out here
// so that we do not play sounds, show lights, etc. for invalid
@@ -1602,136 +1590,14 @@
+ n.getPackageName());
}
- // If we're not supposed to beep, vibrate, etc. then don't.
- if (!mDisableNotificationAlerts
- && (!(old != null
- && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
- && (r.getUserId() == UserHandle.USER_ALL ||
- (r.getUserId() == userId && r.getUserId() == currentUser) ||
- mUserProfiles.isCurrentProfile(r.getUserId()))
- && canInterrupt
- && mSystemReady
- && mAudioManager != null) {
- if (DBG) Slog.v(TAG, "Interrupting!");
- // sound
-
- // should we use the default notification sound? (indicated either by
- // DEFAULT_SOUND or because notification.sound is pointing at
- // Settings.System.NOTIFICATION_SOUND)
- final boolean useDefaultSound =
- (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
- Settings.System.DEFAULT_NOTIFICATION_URI
- .equals(notification.sound);
-
- Uri soundUri = null;
- boolean hasValidSound = false;
-
- if (useDefaultSound) {
- soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
-
- // check to see if the default notification sound is silent
- ContentResolver resolver = getContext().getContentResolver();
- hasValidSound = Settings.System.getString(resolver,
- Settings.System.NOTIFICATION_SOUND) != null;
- } else if (notification.sound != null) {
- soundUri = notification.sound;
- hasValidSound = (soundUri != null);
- }
-
- if (hasValidSound) {
- boolean looping =
- (notification.flags & Notification.FLAG_INSISTENT) != 0;
- int audioStreamType;
- if (notification.audioStreamType >= 0) {
- audioStreamType = notification.audioStreamType;
- } else {
- audioStreamType = DEFAULT_STREAM_TYPE;
- }
- mSoundNotification = r;
- // do not play notifications if stream volume is 0 (typically because
- // ringer mode is silent) or if there is a user of exclusive audio focus
- if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
- && !mAudioManager.isAudioFocusExclusive()) {
- final long identity = Binder.clearCallingIdentity();
- try {
- final IRingtonePlayer player =
- mAudioManager.getRingtonePlayer();
- if (player != null) {
- if (DBG) Slog.v(TAG, "Playing sound " + soundUri
- + " on stream " + audioStreamType);
- player.playAsync(soundUri, user, looping, audioStreamType);
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- // vibrate
- // Does the notification want to specify its own vibration?
- final boolean hasCustomVibrate = notification.vibrate != null;
-
- // new in 4.2: if there was supposed to be a sound and we're in vibrate
- // mode, and no other vibration is specified, we fall back to vibration
- final boolean convertSoundToVibration =
- !hasCustomVibrate
- && hasValidSound
- && (mAudioManager.getRingerMode()
- == AudioManager.RINGER_MODE_VIBRATE);
-
- // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
- final boolean useDefaultVibrate =
- (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
-
- if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
- && !(mAudioManager.getRingerMode()
- == AudioManager.RINGER_MODE_SILENT)) {
- mVibrateNotification = r;
-
- if (useDefaultVibrate || convertSoundToVibration) {
- // Escalate privileges so we can use the vibrator even if the
- // notifying app does not have the VIBRATE permission.
- long identity = Binder.clearCallingIdentity();
- try {
- mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
- useDefaultVibrate ? mDefaultVibrationPattern
- : mFallbackVibrationPattern,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1, notification.audioStreamType);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else if (notification.vibrate.length > 1) {
- // If you want your own vibration pattern, you need the VIBRATE
- // permission
- mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
- notification.vibrate,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1, notification.audioStreamType);
- }
- }
+ // Ensure if this is a foreground service that the proper additional
+ // flags are set.
+ if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ notification.flags |= Notification.FLAG_ONGOING_EVENT
+ | Notification.FLAG_NO_CLEAR;
}
- // light
- // the most recent thing gets the light
- mLights.remove(old);
- if (mLedNotification == old) {
- mLedNotification = null;
- }
- //Slog.i(TAG, "notification.lights="
- // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
- // != 0));
- if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
- && canInterrupt) {
- mLights.add(r);
- updateLightsLocked();
- } else {
- if (old != null
- && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
- updateLightsLocked();
- }
- }
+ buzzBeepBlinkLocked(r);
}
}
});
@@ -1739,6 +1605,158 @@
idOut[0] = id;
}
+ private void buzzBeepBlinkLocked(NotificationRecord record) {
+ final Notification notification = record.sbn.getNotification();
+
+ // Should this notification make noise, vibe, or use the LED?
+ final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
+ !record.isIntercepted();
+ if (DBG || record.isIntercepted())
+ Slog.v(TAG,
+ "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
+ " intercept=" + record.isIntercepted()
+ );
+
+ final int currentUser;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ currentUser = ActivityManager.getCurrentUser();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ // If we're not supposed to beep, vibrate, etc. then don't.
+ if (!mDisableNotificationAlerts
+ && (!(record.isUpdate
+ && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
+ && (record.getUserId() == UserHandle.USER_ALL ||
+ record.getUserId() == currentUser ||
+ mUserProfiles.isCurrentProfile(record.getUserId()))
+ && canInterrupt
+ && mSystemReady
+ && mAudioManager != null) {
+ if (DBG) Slog.v(TAG, "Interrupting!");
+
+ sendAccessibilityEvent(notification, record.sbn.getPackageName());
+
+ // sound
+
+ // should we use the default notification sound? (indicated either by
+ // DEFAULT_SOUND or because notification.sound is pointing at
+ // Settings.System.NOTIFICATION_SOUND)
+ final boolean useDefaultSound =
+ (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
+ Settings.System.DEFAULT_NOTIFICATION_URI
+ .equals(notification.sound);
+
+ Uri soundUri = null;
+ boolean hasValidSound = false;
+
+ if (useDefaultSound) {
+ soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+
+ // check to see if the default notification sound is silent
+ ContentResolver resolver = getContext().getContentResolver();
+ hasValidSound = Settings.System.getString(resolver,
+ Settings.System.NOTIFICATION_SOUND) != null;
+ } else if (notification.sound != null) {
+ soundUri = notification.sound;
+ hasValidSound = (soundUri != null);
+ }
+
+ if (hasValidSound) {
+ boolean looping =
+ (notification.flags & Notification.FLAG_INSISTENT) != 0;
+ int audioStreamType;
+ if (notification.audioStreamType >= 0) {
+ audioStreamType = notification.audioStreamType;
+ } else {
+ audioStreamType = DEFAULT_STREAM_TYPE;
+ }
+ mSoundNotification = record;
+ // do not play notifications if stream volume is 0 (typically because
+ // ringer mode is silent) or if there is a user of exclusive audio focus
+ if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
+ && !mAudioManager.isAudioFocusExclusive()) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player =
+ mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ + " on stream " + audioStreamType);
+ player.playAsync(soundUri, record.sbn.getUser(), looping,
+ audioStreamType);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ // vibrate
+ // Does the notification want to specify its own vibration?
+ final boolean hasCustomVibrate = notification.vibrate != null;
+
+ // new in 4.2: if there was supposed to be a sound and we're in vibrate
+ // mode, and no other vibration is specified, we fall back to vibration
+ final boolean convertSoundToVibration =
+ !hasCustomVibrate
+ && hasValidSound
+ && (mAudioManager.getRingerMode()
+ == AudioManager.RINGER_MODE_VIBRATE);
+
+ // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
+ final boolean useDefaultVibrate =
+ (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
+
+ if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
+ && !(mAudioManager.getRingerMode()
+ == AudioManager.RINGER_MODE_SILENT)) {
+ mVibrateNotification = record;
+
+ if (useDefaultVibrate || convertSoundToVibration) {
+ // Escalate privileges so we can use the vibrator even if the
+ // notifying app does not have the VIBRATE permission.
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ useDefaultVibrate ? mDefaultVibrationPattern
+ : mFallbackVibrationPattern,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1, notification.audioStreamType);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else if (notification.vibrate.length > 1) {
+ // If you want your own vibration pattern, you need the VIBRATE
+ // permission
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ notification.vibrate,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1, notification.audioStreamType);
+ }
+ }
+ }
+
+ // light
+ // release the light
+ boolean wasShowLights = mLights.remove(record.getKey());
+ if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
+ mLedNotification = null;
+ }
+ if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
+ mLights.add(record.getKey());
+ updateLightsLocked();
+ if (mUseAttentionLight) {
+ mAttentionLight.pulse();
+ }
+ } else if (wasShowLights) {
+ updateLightsLocked();
+ }
+ }
+
void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
@@ -1864,6 +1882,9 @@
int indexAfter = findNotificationRecordIndexLocked(record);
boolean interceptAfter = record.isIntercepted();
changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
+ if (interceptBefore && !interceptAfter) {
+ buzzBeepBlinkLocked(record);
+ }
}
if (changed) {
scheduleSendRankingUpdate();
@@ -2009,7 +2030,7 @@
}
// light
- mLights.remove(r);
+ mLights.remove(r.getKey());
if (mLedNotification == r) {
mLedNotification = null;
}
@@ -2194,7 +2215,7 @@
// get next notification, if any
int n = mLights.size();
if (n > 0) {
- mLedNotification = mLights.get(n-1);
+ mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 08f8eb4..13fb986 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -42,6 +42,7 @@
final StatusBarNotification sbn;
NotificationUsageStats.SingleNotificationStats stats;
boolean isCanceled;
+ int score;
// These members are used by NotificationSignalExtractors
// to communicate with the ranking module.
@@ -53,9 +54,17 @@
// InterceptedNotifications needs to know if this has been previously evaluated.
private boolean mTouchedByZen;
- NotificationRecord(StatusBarNotification sbn)
+ // The timestamp used for ranking.
+ private long mRankingTimeMs;
+
+ // Is this record an update of an old record?
+ public boolean isUpdate;
+
+ NotificationRecord(StatusBarNotification sbn, int score)
{
this.sbn = sbn;
+ this.score = score;
+ mRankingTimeMs = calculateRankingTimeMs(0L);
}
// copy any notes that the ranking system may have made before the update
@@ -64,6 +73,7 @@
mRecentlyIntrusive = previous.mRecentlyIntrusive;
mTouchedByZen = previous.mTouchedByZen;
mIntercept = previous.mIntercept;
+ mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
}
public Notification getNotification() { return sbn.getNotification(); }
@@ -134,6 +144,7 @@
pw.println(prefix + " mContactAffinity=" + mContactAffinity);
pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive);
pw.println(prefix + " mIntercept=" + mIntercept);
+ pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs);
}
@@ -201,4 +212,29 @@
mTouchedByZen = true;
}
+ /**
+ * Returns the timestamp to use for time-based sorting in the ranker.
+ */
+ public long getRankingTimeMs() {
+ return mRankingTimeMs;
+ }
+
+ /**
+ * @param previousRankingTimeMs for updated notifications, {@link #getRankingTimeMs()}
+ * of the previous notification record, 0 otherwise
+ */
+ private long calculateRankingTimeMs(long previousRankingTimeMs) {
+ Notification n = getNotification();
+ // Take developer provided 'when', unless it's in the future.
+ if (n.when != 0 && n.when <= sbn.getPostTime()) {
+ return n.when;
+ }
+ // If we've ranked a previous instance with a timestamp, inherit it. This case is
+ // important in order to have ranking stability for updating notifications.
+ if (previousRankingTimeMs > 0) {
+ return previousRankingTimeMs;
+ }
+ return sbn.getPostTime();
+ }
+
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 50a32c4..b95db9c 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -18,6 +18,7 @@
import android.app.AlarmManager;
import android.app.AppOpsManager;
+import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -88,6 +89,10 @@
private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.deskclock"
));
+ private static final Set<String> SYSTEM_PACKAGES = new HashSet<String>(Arrays.asList(
+ "android",
+ "com.android.systemui"
+ ));
public ZenModeHelper(Context context, Handler handler) {
mContext = context;
@@ -130,6 +135,9 @@
// notifications never transition from not intercepted to intercepted
return false;
}
+ if (isSystem(record)) {
+ return false;
+ }
if (isAlarm(record)) {
return false;
}
@@ -239,6 +247,11 @@
}
}
+ private boolean isSystem(NotificationRecord record) {
+ return SYSTEM_PACKAGES.contains(record.sbn.getPackageName())
+ && Notification.CATEGORY_SYSTEM.equals(record.getNotification().category);
+ }
+
private boolean isAlarm(NotificationRecord record) {
return ALARM_PACKAGES.contains(record.sbn.getPackageName());
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index dc60853..49502b6 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4991,6 +4991,7 @@
mResolveActivity.packageName = mAndroidApplication.packageName;
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+ mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
mResolveActivity.exported = true;
@@ -5398,12 +5399,23 @@
if (pkg.applicationInfo.nativeLibraryDir != null) {
final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
try {
+ // Enable gross and lame hacks for apps that are built with old
+ // SDK tools. We must scan their APKs for renderscript bitcode and
+ // not launch them if it's present. Don't bother checking on devices
+ // that don't have 64 bit support.
+ String[] abiList = Build.SUPPORTED_ABIS;
+ boolean hasLegacyRenderscriptBitcode = false;
+ if (abiOverride != null) {
+ abiList = new String[] { abiOverride };
+ } else if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ hasLegacyRenderscriptBitcode = true;
+ }
+
File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
final String dataPathString = dataPath.getCanonicalPath();
- final String[] abiList = (abiOverride != null) ? new String[] { abiOverride } :
- Build.SUPPORTED_ABIS;
-
if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
/*
* Upgrading from a previous version of the OS sometimes
@@ -5416,17 +5428,18 @@
Log.i(TAG, "removed obsolete native libraries for system package "
+ path);
}
- if (abiOverride != null) {
- pkg.applicationInfo.cpuAbi = abiOverride;
+ if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
+ pkgSetting.cpuAbiString = abiList[0];
} else {
setInternalAppAbi(pkg, pkgSetting);
}
} else {
if (!isForwardLocked(pkg) && !isExternal(pkg)) {
/*
- * Update native library dir if it starts with
- * /data/data
- */
+ * Update native library dir if it starts with
+ * /data/data
+ */
if (nativeLibraryDir.getPath().startsWith(dataPathString)) {
setInternalAppNativeLibraryPath(pkg, pkgSetting);
nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
@@ -5445,8 +5458,8 @@
// note of what ABI we're using
if (copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
pkg.applicationInfo.cpuAbi = abiList[copyRet];
- } else if (abiOverride != null) {
- pkg.applicationInfo.cpuAbi = abiOverride;
+ } else if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
} else {
pkg.applicationInfo.cpuAbi = null;
}
@@ -5470,8 +5483,8 @@
// Note that (non upgraded) system apps will not have any native
// libraries bundled in their APK, but we're guaranteed not to be
// such an app at this point.
- if (abiOverride != null) {
- pkg.applicationInfo.cpuAbi = abiOverride;
+ if (abiOverride != null || hasLegacyRenderscriptBitcode) {
+ pkg.applicationInfo.cpuAbi = abiList[0];
} else {
pkg.applicationInfo.cpuAbi = null;
}
@@ -7817,13 +7830,9 @@
@Override
public boolean getApplicationBlockedSettingAsUser(String packageName, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, true,
+ "getApplicationBlocked for user " + userId);
PackageSetting pkgSetting;
- final int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != userId) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "getApplicationBlocked for user " + userId);
- }
long callingId = Binder.clearCallingIdentity();
try {
// writer
@@ -9215,9 +9224,15 @@
}
final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(codeFile);
- final String[] abiList = (abiOverride != null) ?
+ String[] abiList = (abiOverride != null) ?
new String[] { abiOverride } : Build.SUPPORTED_ABIS;
try {
+ if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
+ abiOverride == null &&
+ NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
+ abiList = Build.SUPPORTED_32_BIT_ABIS;
+ }
+
int copyRet = copyNativeLibrariesForInternalApp(handle, nativeLibraryFile, abiList);
if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
return copyRet;
@@ -10251,7 +10266,7 @@
synchronized (mPackages) {
// Check whether the newly-scanned package wants to define an already-defined perm
int N = pkg.permissions.size();
- for (int i = 0; i < N; i++) {
+ for (int i = N-1; i >= 0; i--) {
PackageParser.Permission perm = pkg.permissions.get(i);
BasePermission bp = mSettings.mPermissions.get(perm.info.name);
if (bp != null) {
@@ -10259,13 +10274,23 @@
// also includes the "updating the same package" case, of course.
if (compareSignatures(bp.packageSetting.signatures.mSignatures,
pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
- Slog.w(TAG, "Package " + pkg.packageName
- + " attempting to redeclare permission " + perm.info.name
- + " already owned by " + bp.sourcePackage);
- res.returnCode = PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
- res.origPermission = perm.info.name;
- res.origPackage = bp.sourcePackage;
- return;
+ // If the owning package is the system itself, we log but allow
+ // install to proceed; we fail the install on all other permission
+ // redefinitions.
+ if (!bp.sourcePackage.equals("android")) {
+ Slog.w(TAG, "Package " + pkg.packageName
+ + " attempting to redeclare permission " + perm.info.name
+ + " already owned by " + bp.sourcePackage);
+ res.returnCode = PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
+ res.origPermission = perm.info.name;
+ res.origPackage = bp.sourcePackage;
+ return;
+ } else {
+ Slog.w(TAG, "Package " + pkg.packageName
+ + " attempting to redeclare system permission "
+ + perm.info.name + "; ignoring new declaration");
+ pkg.permissions.remove(i);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7162683..0cb2ab9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -161,7 +161,6 @@
private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray();
private int[] mUserIds;
- private boolean mGuestEnabled;
private int mNextSerialNumber;
private int mUserVersion = 0;
@@ -427,43 +426,6 @@
}
}
- @Override
- public void setGuestEnabled(boolean enable) {
- checkManageUsersPermission("enable guest users");
- synchronized (mPackagesLock) {
- if (mGuestEnabled != enable) {
- mGuestEnabled = enable;
- // Erase any guest user that currently exists
- for (int i = 0; i < mUsers.size(); i++) {
- UserInfo user = mUsers.valueAt(i);
- if (!user.partial && user.isGuest()) {
- if (!enable) {
- removeUser(user.id);
- }
- return;
- }
- }
- // No guest was found
- if (enable) {
- createUser("Guest", UserInfo.FLAG_GUEST);
- }
- }
- }
- }
-
- @Override
- public boolean isGuestEnabled() {
- synchronized (mPackagesLock) {
- return mGuestEnabled;
- }
- }
-
- @Override
- public void wipeUser(int userHandle) {
- checkManageUsersPermission("wipe user");
- // TODO:
- }
-
public void makeInitialized(int userId) {
checkManageUsersPermission("makeInitialized");
synchronized (mPackagesLock) {
@@ -583,7 +545,6 @@
}
private void readUserListLocked() {
- mGuestEnabled = false;
if (!mUserListFile.exists()) {
fallbackToSingleUserLocked();
return;
@@ -625,9 +586,6 @@
if (user != null) {
mUsers.put(user.id, user);
- if (user.isGuest()) {
- mGuestEnabled = true;
- }
if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
mNextSerialNumber = user.id + 1;
}
@@ -1273,7 +1231,8 @@
private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
Intent managedProfileIntent = new Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
+ Intent.FLAG_RECEIVER_FOREGROUND);
managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId));
mContext.sendBroadcastAsUser(managedProfileIntent, new UserHandle(parentUserId), null);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index aca17bf..fb4b8f0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -406,6 +406,15 @@
// If true, the device is in low power mode.
private boolean mLowPowerModeEnabled;
+ // Current state of the low power mode setting.
+ private boolean mLowPowerModeSetting;
+
+ // Current state of whether the settings are allowing auto low power mode.
+ private boolean mAutoLowPowerModeEnabled;
+
+ // True if the battery level is currently considered low.
+ private boolean mBatteryLevelLow;
+
private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners
= new ArrayList<PowerManagerInternal.LowPowerModeListener>();
@@ -502,6 +511,7 @@
// Register for broadcasts from other components of the system.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
filter = new IntentFilter();
@@ -551,6 +561,9 @@
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.LOW_POWER_MODE),
false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+ false, mSettingsObserver, UserHandle.USER_ALL);
// Go.
readConfigurationLocked();
updateSettingsLocked();
@@ -638,7 +651,23 @@
final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE, 0) != 0;
- if (lowPowerModeEnabled != mLowPowerModeEnabled) {
+ final boolean autoLowPowerModeEnabled = Settings.Global.getInt(resolver,
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 15) != 0;
+ if (lowPowerModeEnabled != mLowPowerModeSetting
+ || autoLowPowerModeEnabled != mAutoLowPowerModeEnabled) {
+ mLowPowerModeSetting = lowPowerModeEnabled;
+ mAutoLowPowerModeEnabled = autoLowPowerModeEnabled;
+ updateLowPowerModeLocked();
+ }
+
+ mDirty |= DIRTY_SETTINGS;
+ }
+
+ void updateLowPowerModeLocked() {
+ final boolean lowPowerModeEnabled = !mIsPowered
+ && (mLowPowerModeSetting || (mAutoLowPowerModeEnabled && mBatteryLevelLow));
+ if (mLowPowerModeEnabled != lowPowerModeEnabled) {
+ mLowPowerModeEnabled = lowPowerModeEnabled;
powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
mLowPowerModeEnabled = lowPowerModeEnabled;
BackgroundThread.getHandler().post(new Runnable() {
@@ -652,11 +681,12 @@
for (int i=0; i<listeners.size(); i++) {
listeners.get(i).onLowPowerModeChanged(lowPowerModeEnabled);
}
+ Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcast(intent);
}
});
}
-
- mDirty |= DIRTY_SETTINGS;
}
private void handleSettingsChangedLocked() {
@@ -1137,9 +1167,11 @@
if ((dirty & DIRTY_BATTERY_STATE) != 0) {
final boolean wasPowered = mIsPowered;
final int oldPlugType = mPlugType;
+ final boolean oldLevelLow = mBatteryLevelLow;
mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
mPlugType = mBatteryService.getPlugType();
mBatteryLevel = mBatteryService.getBatteryLevel();
+ mBatteryLevelLow = mBatteryService.getBatteryLevelLow();
if (DEBUG_SPEW) {
Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
@@ -1175,6 +1207,10 @@
mNotifier.onWirelessChargingStarted();
}
}
+
+ if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {
+ updateLowPowerModeLocked();
+ }
}
}
@@ -1896,6 +1932,12 @@
}
}
+ private boolean isLowPowerModeInternal() {
+ synchronized (mLock) {
+ return mLowPowerModeEnabled;
+ }
+ }
+
private void handleBatteryStateChangedLocked() {
mDirty |= DIRTY_BATTERY_STATE;
updatePowerStateLocked();
@@ -2137,6 +2179,8 @@
pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity);
pw.println(" mSandmanScheduled=" + mSandmanScheduled);
pw.println(" mSandmanSummoned=" + mSandmanSummoned);
+ pw.println(" mLowPowerModeEnabled=" + mLowPowerModeEnabled);
+ pw.println(" mBatteryLevelLow=" + mBatteryLevelLow);
pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
@@ -2173,6 +2217,8 @@
pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting);
pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting);
pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
+ pw.println(" mLowPowerModeSetting=" + mLowPowerModeSetting);
+ pw.println(" mAutoLowPowerModeEnabled=" + mAutoLowPowerModeEnabled);
pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig);
pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig);
pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig);
@@ -2764,6 +2810,16 @@
}
}
+ @Override // Binder call
+ public boolean isPowerSaveMode() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isLowPowerModeInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
/**
* Reboots the device.
*
diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/task/StateChangedListener.java
deleted file mode 100644
index b1a4636..0000000
--- a/services/core/java/com/android/server/task/StateChangedListener.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task;
-
-import com.android.server.task.controllers.TaskStatus;
-
-/**
- * Interface through which a {@link com.android.server.task.controllers.StateController} informs
- * the {@link com.android.server.task.TaskManagerService} that there are some tasks potentially
- * ready to be run.
- */
-public interface StateChangedListener {
- /**
- * Called by the controller to notify the TaskManager that it should check on the state of a
- * task.
- */
- public void onControllerStateChanged();
-
- /**
- * Called by the controller to notify the TaskManager that regardless of the state of the task,
- * it must be run immediately.
- * @param taskStatus The state of the task which is to be run immediately.
- */
- public void onTaskDeadlineExpired(TaskStatus taskStatus);
-}
diff --git a/services/core/java/com/android/server/task/TaskCompletedListener.java b/services/core/java/com/android/server/task/TaskCompletedListener.java
deleted file mode 100644
index c53f5ca..0000000
--- a/services/core/java/com/android/server/task/TaskCompletedListener.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task;
-
-import com.android.server.task.controllers.TaskStatus;
-
-/**
- * Used for communication between {@link com.android.server.task.TaskServiceContext} and the
- * {@link com.android.server.task.TaskManagerService}.
- */
-public interface TaskCompletedListener {
-
- /**
- * Callback for when a task is completed.
- * @param needsReschedule Whether the implementing class should reschedule this task.
- */
- public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule);
-}
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
deleted file mode 100644
index d5b70e6..0000000
--- a/services/core/java/com/android/server/task/TaskManagerService.java
+++ /dev/null
@@ -1,629 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import android.app.task.ITaskManager;
-import android.app.task.Task;
-import android.app.task.TaskManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.server.task.controllers.ConnectivityController;
-import com.android.server.task.controllers.IdleController;
-import com.android.server.task.controllers.StateController;
-import com.android.server.task.controllers.TaskStatus;
-import com.android.server.task.controllers.TimeController;
-
-import java.util.LinkedList;
-
-/**
- * Responsible for taking tasks representing work to be performed by a client app, and determining
- * based on the criteria specified when that task should be run against the client application's
- * endpoint.
- * @hide
- */
-public class TaskManagerService extends com.android.server.SystemService
- implements StateChangedListener, TaskCompletedListener {
- // TODO: Switch this off for final version.
- private static final boolean DEBUG = true;
- /** The number of concurrent tasks we run at one time. */
- private static final int MAX_TASK_CONTEXTS_COUNT = 3;
- static final String TAG = "TaskManager";
- /**
- * When a task fails, it gets rescheduled according to its backoff policy. To be nice, we allow
- * this amount of time from the rescheduled time by which the retry must occur.
- */
- private static final long RESCHEDULE_WINDOW_SLOP_MILLIS = 5000L;
-
- /** Master list of tasks. */
- private final TaskStore mTasks;
-
- static final int MSG_TASK_EXPIRED = 0;
- static final int MSG_CHECK_TASKS = 1;
-
- // Policy constants
- /**
- * Minimum # of idle tasks that must be ready in order to force the TM to schedule things
- * early.
- */
- private static final int MIN_IDLE_COUNT = 1;
- /**
- * Minimum # of connectivity tasks that must be ready in order to force the TM to schedule
- * things early.
- */
- private static final int MIN_CONNECTIVITY_COUNT = 2;
- /**
- * Minimum # of tasks (with no particular constraints) for which the TM will be happy running
- * some work early.
- */
- private static final int MIN_READY_TASKS_COUNT = 4;
-
- /**
- * Track Services that have currently active or pending tasks. The index is provided by
- * {@link TaskStatus#getServiceToken()}
- */
- private final List<TaskServiceContext> mActiveServices = new LinkedList<TaskServiceContext>();
- /** List of controllers that will notify this service of updates to tasks. */
- private List<StateController> mControllers;
- /**
- * Queue of pending tasks. The TaskServiceContext class will receive tasks from this list
- * when ready to execute them.
- */
- private final LinkedList<TaskStatus> mPendingTasks = new LinkedList<TaskStatus>();
-
- private final TaskHandler mHandler;
- private final TaskManagerStub mTaskManagerStub;
-
- /**
- * Entry point from client to schedule the provided task.
- * This will add the task to the
- * @param task Task object containing execution parameters
- * @param uId The package identifier of the application this task is for.
- * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
- * of this task.
- * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
- */
- public int schedule(Task task, int uId, boolean canPersistTask) {
- TaskStatus taskStatus = new TaskStatus(task, uId, canPersistTask);
- return startTrackingTask(taskStatus) ?
- TaskManager.RESULT_SUCCESS : TaskManager.RESULT_FAILURE;
- }
-
- public List<Task> getPendingTasks(int uid) {
- ArrayList<Task> outList = new ArrayList<Task>();
- synchronized (mTasks) {
- for (TaskStatus ts : mTasks.getTasks()) {
- if (ts.getUid() == uid) {
- outList.add(ts.getTask());
- }
- }
- }
- return outList;
- }
-
- /**
- * Entry point from client to cancel all tasks originating from their uid.
- * This will remove the task from the master list, and cancel the task if it was staged for
- * execution or being executed.
- * @param uid To check against for removal of a task.
- */
- public void cancelTaskForUid(int uid) {
- // Remove from master list.
- synchronized (mTasks) {
- if (!mTasks.removeAllByUid(uid)) {
- // If it's not in the master list, it's nowhere.
- return;
- }
- }
- // Remove from pending queue.
- synchronized (mPendingTasks) {
- Iterator<TaskStatus> it = mPendingTasks.iterator();
- while (it.hasNext()) {
- TaskStatus ts = it.next();
- if (ts.getUid() == uid) {
- it.remove();
- }
- }
- }
- // Cancel if running.
- synchronized (mActiveServices) {
- for (TaskServiceContext tsc : mActiveServices) {
- if (tsc.getRunningTask().getUid() == uid) {
- tsc.cancelExecutingTask();
- }
- }
- }
- }
-
- /**
- * Entry point from client to cancel the task corresponding to the taskId provided.
- * This will remove the task from the master list, and cancel the task if it was staged for
- * execution or being executed.
- * @param uid Uid of the calling client.
- * @param taskId Id of the task, provided at schedule-time.
- */
- public void cancelTask(int uid, int taskId) {
- synchronized (mTasks) {
- if (!mTasks.remove(uid, taskId)) {
- // If it's not in the master list, it's nowhere.
- return;
- }
- }
- synchronized (mPendingTasks) {
- Iterator<TaskStatus> it = mPendingTasks.iterator();
- while (it.hasNext()) {
- TaskStatus ts = it.next();
- if (ts.getUid() == uid && ts.getTaskId() == taskId) {
- it.remove();
- // If we got it from pending, it didn't make it to active so return.
- return;
- }
- }
- }
- synchronized (mActiveServices) {
- for (TaskServiceContext tsc : mActiveServices) {
- if (tsc.getRunningTask().getUid() == uid &&
- tsc.getRunningTask().getTaskId() == taskId) {
- tsc.cancelExecutingTask();
- return;
- }
- }
- }
- }
-
- /**
- * Initializes the system service.
- * <p>
- * Subclasses must define a single argument constructor that accepts the context
- * and passes it to super.
- * </p>
- *
- * @param context The system server context.
- */
- public TaskManagerService(Context context) {
- super(context);
- mTasks = new TaskStore(context);
- mHandler = new TaskHandler(context.getMainLooper());
- mTaskManagerStub = new TaskManagerStub();
- // Create the "runners".
- for (int i = 0; i < MAX_TASK_CONTEXTS_COUNT; i++) {
- mActiveServices.add(
- new TaskServiceContext(this, context.getMainLooper()));
- }
-
- mControllers = new LinkedList<StateController>();
- mControllers.add(ConnectivityController.get(this));
- mControllers.add(TimeController.get(this));
- mControllers.add(IdleController.get(this));
- // TODO: Add BatteryStateController when implemented.
- }
-
- @Override
- public void onStart() {
- publishBinderService(Context.TASK_SERVICE, mTaskManagerStub);
- }
-
- /**
- * Called when we have a task status object that we need to insert in our
- * {@link com.android.server.task.TaskStore}, and make sure all the relevant controllers know
- * about.
- */
- private boolean startTrackingTask(TaskStatus taskStatus) {
- boolean added = false;
- synchronized (mTasks) {
- added = mTasks.add(taskStatus);
- }
- if (added) {
- for (StateController controller : mControllers) {
- controller.maybeStartTrackingTask(taskStatus);
- }
- }
- return added;
- }
-
- /**
- * Called when we want to remove a TaskStatus object that we've finished executing. Returns the
- * object removed.
- */
- private boolean stopTrackingTask(TaskStatus taskStatus) {
- boolean removed;
- synchronized (mTasks) {
- // Remove from store as well as controllers.
- removed = mTasks.remove(taskStatus);
- }
- if (removed) {
- for (StateController controller : mControllers) {
- controller.maybeStopTrackingTask(taskStatus);
- }
- }
- return removed;
- }
-
- private boolean cancelTaskOnServiceContext(TaskStatus ts) {
- synchronized (mActiveServices) {
- for (TaskServiceContext tsc : mActiveServices) {
- if (tsc.getRunningTask() == ts) {
- tsc.cancelExecutingTask();
- return true;
- }
- }
- return false;
- }
- }
-
- /**
- * @param ts TaskStatus we are querying against.
- * @return Whether or not the task represented by the status object is currently being run or
- * is pending.
- */
- private boolean isCurrentlyActive(TaskStatus ts) {
- synchronized (mActiveServices) {
- for (TaskServiceContext serviceContext : mActiveServices) {
- if (serviceContext.getRunningTask() == ts) {
- return true;
- }
- }
- return false;
- }
- }
-
- /**
- * A task is rescheduled with exponential back-off if the client requests this from their
- * execution logic.
- * A caveat is for idle-mode tasks, for which the idle-mode constraint will usurp the
- * timeliness of the reschedule. For an idle-mode task, no deadline is given.
- * @param failureToReschedule Provided task status that we will reschedule.
- * @return A newly instantiated TaskStatus with the same constraints as the last task except
- * with adjusted timing constraints.
- */
- private TaskStatus getRescheduleTaskForFailure(TaskStatus failureToReschedule) {
- final long elapsedNowMillis = SystemClock.elapsedRealtime();
- final Task task = failureToReschedule.getTask();
-
- final long initialBackoffMillis = task.getInitialBackoffMillis();
- final int backoffAttempt = failureToReschedule.getNumFailures() + 1;
- long newEarliestRuntimeElapsed = elapsedNowMillis;
-
- switch (task.getBackoffPolicy()) {
- case Task.BackoffPolicy.LINEAR:
- newEarliestRuntimeElapsed += initialBackoffMillis * backoffAttempt;
- break;
- default:
- if (DEBUG) {
- Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
- }
- case Task.BackoffPolicy.EXPONENTIAL:
- newEarliestRuntimeElapsed += Math.pow(initialBackoffMillis, backoffAttempt);
- break;
- }
- long newLatestRuntimeElapsed = failureToReschedule.hasIdleConstraint() ? Long.MAX_VALUE
- : newEarliestRuntimeElapsed + RESCHEDULE_WINDOW_SLOP_MILLIS;
- return new TaskStatus(failureToReschedule, newEarliestRuntimeElapsed,
- newLatestRuntimeElapsed, backoffAttempt);
- }
-
- /**
- * Called after a periodic has executed so we can to re-add it. We take the last execution time
- * of the task to be the time of completion (i.e. the time at which this function is called).
- * This could be inaccurate b/c the task can run for as long as
- * {@link com.android.server.task.TaskServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
- * to underscheduling at least, rather than if we had taken the last execution time to be the
- * start of the execution.
- * @return A new task representing the execution criteria for this instantiation of the
- * recurring task.
- */
- private TaskStatus getRescheduleTaskForPeriodic(TaskStatus periodicToReschedule) {
- final long elapsedNow = SystemClock.elapsedRealtime();
- // Compute how much of the period is remaining.
- long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
- long newEarliestRunTimeElapsed = elapsedNow + runEarly;
- long period = periodicToReschedule.getTask().getIntervalMillis();
- long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
-
- if (DEBUG) {
- Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
- newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
- }
- return new TaskStatus(periodicToReschedule, newEarliestRunTimeElapsed,
- newLatestRuntimeElapsed, 0 /* backoffAttempt */);
- }
-
- // TaskCompletedListener implementations.
-
- /**
- * A task just finished executing. We fetch the
- * {@link com.android.server.task.controllers.TaskStatus} from the store and depending on
- * whether we want to reschedule we readd it to the controllers.
- * @param taskStatus Completed task.
- * @param needsReschedule Whether the implementing class should reschedule this task.
- */
- @Override
- public void onTaskCompleted(TaskStatus taskStatus, boolean needsReschedule) {
- if (!stopTrackingTask(taskStatus)) {
- if (DEBUG) {
- Slog.e(TAG, "Error removing task: could not find task to remove. Was task" +
- "removed while executing?");
- }
- return;
- }
- if (needsReschedule) {
- TaskStatus rescheduled = getRescheduleTaskForFailure(taskStatus);
- startTrackingTask(rescheduled);
- } else if (taskStatus.getTask().isPeriodic()) {
- TaskStatus rescheduledPeriodic = getRescheduleTaskForPeriodic(taskStatus);
- startTrackingTask(rescheduledPeriodic);
- }
- mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
- }
-
- // StateChangedListener implementations.
-
- /**
- * Off-board work to our handler thread as quickly as possible, b/c this call is probably being
- * made on the main thread.
- * For now this takes the task and if it's ready to run it will run it. In future we might not
- * provide the task, so that the StateChangedListener has to run through its list of tasks to
- * see which are ready. This will further decouple the controllers from the execution logic.
- */
- @Override
- public void onControllerStateChanged() {
- // Post a message to to run through the list of tasks and start/stop any that are eligible.
- mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
- }
-
- @Override
- public void onTaskDeadlineExpired(TaskStatus taskStatus) {
- mHandler.obtainMessage(MSG_TASK_EXPIRED, taskStatus);
- }
-
- private class TaskHandler extends Handler {
-
- public TaskHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_TASK_EXPIRED:
- final TaskStatus expired = (TaskStatus) message.obj; // Unused for now.
- queueReadyTasksForExecutionH();
- break;
- case MSG_CHECK_TASKS:
- // Check the list of tasks and run some of them if we feel inclined.
- maybeQueueReadyTasksForExecutionH();
- break;
- }
- maybeRunNextPendingTaskH();
- // Don't remove TASK_EXPIRED in case one came along while processing the queue.
- removeMessages(MSG_CHECK_TASKS);
- }
-
- /**
- * Run through list of tasks and execute all possible - at least one is expired so we do
- * as many as we can.
- */
- private void queueReadyTasksForExecutionH() {
- synchronized (mTasks) {
- for (TaskStatus ts : mTasks.getTasks()) {
- final boolean criteriaSatisfied = ts.isReady();
- final boolean isRunning = isCurrentlyActive(ts);
- if (criteriaSatisfied && !isRunning) {
- synchronized (mPendingTasks) {
- mPendingTasks.add(ts);
- }
- } else if (!criteriaSatisfied && isRunning) {
- cancelTaskOnServiceContext(ts);
- }
- }
- }
- }
-
- /**
- * The state of at least one task has changed. Here is where we could enforce various
- * policies on when we want to execute tasks.
- * Right now the policy is such:
- * If >1 of the ready tasks is idle mode we send all of them off
- * if more than 2 network connectivity tasks are ready we send them all off.
- * If more than 4 tasks total are ready we send them all off.
- * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
- */
- private void maybeQueueReadyTasksForExecutionH() {
- synchronized (mTasks) {
- int idleCount = 0;
- int connectivityCount = 0;
- List<TaskStatus> runnableTasks = new ArrayList<TaskStatus>();
- for (TaskStatus ts : mTasks.getTasks()) {
- final boolean criteriaSatisfied = ts.isReady();
- final boolean isRunning = isCurrentlyActive(ts);
- if (criteriaSatisfied && !isRunning) {
- if (ts.hasIdleConstraint()) {
- idleCount++;
- }
- if (ts.hasConnectivityConstraint() || ts.hasMeteredConstraint()) {
- connectivityCount++;
- }
- runnableTasks.add(ts);
- } else if (!criteriaSatisfied && isRunning) {
- cancelTaskOnServiceContext(ts);
- }
- }
- if (idleCount >= MIN_IDLE_COUNT || connectivityCount >= MIN_CONNECTIVITY_COUNT ||
- runnableTasks.size() >= MIN_READY_TASKS_COUNT) {
- for (TaskStatus ts : runnableTasks) {
- synchronized (mPendingTasks) {
- mPendingTasks.add(ts);
- }
- }
- }
- }
- }
-
- /**
- * Checks the state of the pending queue against any available
- * {@link com.android.server.task.TaskServiceContext} that can run a new task.
- * {@link com.android.server.task.TaskServiceContext}.
- */
- private void maybeRunNextPendingTaskH() {
- TaskStatus nextPending;
- synchronized (mPendingTasks) {
- nextPending = mPendingTasks.poll();
- }
- if (nextPending == null) {
- return;
- }
-
- synchronized (mActiveServices) {
- for (TaskServiceContext tsc : mActiveServices) {
- if (tsc.isAvailable()) {
- if (tsc.executeRunnableTask(nextPending)) {
- return;
- }
- }
- }
- }
- }
- }
-
- /**
- * Binder stub trampoline implementation
- */
- final class TaskManagerStub extends ITaskManager.Stub {
- /** Cache determination of whether a given app can persist tasks
- * key is uid of the calling app; value is undetermined/true/false
- */
- private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
-
- // Determine whether the caller is allowed to persist tasks, with a small cache
- // because the lookup is expensive enough that we'd like to avoid repeating it.
- // This must be called from within the calling app's binder identity!
- private boolean canCallerPersistTasks() {
- final boolean canPersist;
- final int callingUid = Binder.getCallingUid();
- synchronized (mPersistCache) {
- Boolean cached = mPersistCache.get(callingUid);
- if (cached) {
- canPersist = cached.booleanValue();
- } else {
- // Persisting tasks is tantamount to running at boot, so we permit
- // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
- // permission
- int result = getContext().checkCallingPermission(
- android.Manifest.permission.RECEIVE_BOOT_COMPLETED);
- canPersist = (result == PackageManager.PERMISSION_GRANTED);
- mPersistCache.put(callingUid, canPersist);
- }
- }
- return canPersist;
- }
-
- // ITaskManager implementation
- @Override
- public int schedule(Task task) throws RemoteException {
- final boolean canPersist = canCallerPersistTasks();
- final int uid = Binder.getCallingUid();
-
- long ident = Binder.clearCallingIdentity();
- try {
- return TaskManagerService.this.schedule(task, uid, canPersist);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public List<Task> getAllPendingTasks() throws RemoteException {
- final int uid = Binder.getCallingUid();
-
- long ident = Binder.clearCallingIdentity();
- try {
- return TaskManagerService.this.getPendingTasks(uid);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public void cancelAll() throws RemoteException {
- final int uid = Binder.getCallingUid();
-
- long ident = Binder.clearCallingIdentity();
- try {
- TaskManagerService.this.cancelTaskForUid(uid);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override
- public void cancel(int taskId) throws RemoteException {
- final int uid = Binder.getCallingUid();
-
- long ident = Binder.clearCallingIdentity();
- try {
- TaskManagerService.this.cancelTask(uid, taskId);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
- * "dumpsys" infrastructure
- */
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
-
- long identityToken = Binder.clearCallingIdentity();
- try {
- TaskManagerService.this.dumpInternal(pw);
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- }
- };
-
- void dumpInternal(PrintWriter pw) {
- synchronized (mTasks) {
- pw.print("Registered tasks:");
- if (mTasks.size() > 0) {
- for (TaskStatus ts : mTasks.getTasks()) {
- pw.println();
- ts.dump(pw, " ");
- }
- } else {
- pw.println();
- pw.println("No tasks scheduled.");
- }
- }
- pw.println();
- }
-}
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
deleted file mode 100644
index 75e9212..0000000
--- a/services/core/java/com/android/server/task/TaskServiceContext.java
+++ /dev/null
@@ -1,503 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task;
-
-import android.app.ActivityManager;
-import android.app.task.ITaskCallback;
-import android.app.task.ITaskService;
-import android.app.task.TaskParams;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.task.controllers.TaskStatus;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Handles client binding and lifecycle of a task. A task will only execute one at a time on an
- * instance of this class.
- */
-public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection {
- private static final boolean DEBUG = true;
- private static final String TAG = "TaskServiceContext";
- /** Define the maximum # of tasks allowed to run on a service at once. */
- private static final int defaultMaxActiveTasksPerService =
- ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
- /** Amount of time a task is allowed to execute for before being considered timed-out. */
- private static final long EXECUTING_TIMESLICE_MILLIS = 5 * 60 * 1000;
- /** Amount of time the TaskManager will wait for a response from an app for a message. */
- private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
- /** String prefix for all wakelock names. */
- private static final String TM_WAKELOCK_PREFIX = "*task*/";
-
- private static final String[] VERB_STRINGS = {
- "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_PENDING"
- };
-
- // States that a task occupies while interacting with the client.
- static final int VERB_BINDING = 0;
- static final int VERB_STARTING = 1;
- static final int VERB_EXECUTING = 2;
- static final int VERB_STOPPING = 3;
-
- // Messages that result from interactions with the client service.
- /** System timed out waiting for a response. */
- private static final int MSG_TIMEOUT = 0;
- /** Received a callback from client. */
- private static final int MSG_CALLBACK = 1;
- /** Run through list and start any ready tasks.*/
- private static final int MSG_SERVICE_BOUND = 2;
- /** Cancel a task. */
- private static final int MSG_CANCEL = 3;
- /** Shutdown the Task. Used when the client crashes and we can't die gracefully.*/
- private static final int MSG_SHUTDOWN_EXECUTION = 4;
-
- private final Handler mCallbackHandler;
- /** Make callbacks to {@link TaskManagerService} to inform on task completion status. */
- private final TaskCompletedListener mCompletedListener;
- /** Used for service binding, etc. */
- private final Context mContext;
- private PowerManager.WakeLock mWakeLock;
-
- // Execution state.
- private TaskParams mParams;
- @VisibleForTesting
- int mVerb;
- private AtomicBoolean mCancelled = new AtomicBoolean();
-
- /** All the information maintained about the task currently being executed. */
- private TaskStatus mRunningTask;
- /** Binder to the client service. */
- ITaskService service;
-
- private final Object mAvailableLock = new Object();
- /** Whether this context is free. */
- @GuardedBy("mAvailableLock")
- private boolean mAvailable;
-
- TaskServiceContext(TaskManagerService service, Looper looper) {
- this(service.getContext(), service, looper);
- }
-
- @VisibleForTesting
- TaskServiceContext(Context context, TaskCompletedListener completedListener, Looper looper) {
- mContext = context;
- mCallbackHandler = new TaskServiceHandler(looper);
- mCompletedListener = completedListener;
- }
-
- /**
- * Give a task to this context for execution. Callers must first check {@link #isAvailable()}
- * to make sure this is a valid context.
- * @param ts The status of the task that we are going to run.
- * @return True if the task was accepted and is going to run.
- */
- boolean executeRunnableTask(TaskStatus ts) {
- synchronized (mAvailableLock) {
- if (!mAvailable) {
- Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
- return false;
- }
- mAvailable = false;
- }
-
- final PowerManager pm =
- (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- TM_WAKELOCK_PREFIX + ts.getServiceComponent().getPackageName());
- mWakeLock.setWorkSource(new WorkSource(ts.getUid()));
- mWakeLock.setReferenceCounted(false);
-
- mRunningTask = ts;
- mParams = new TaskParams(ts.getTaskId(), ts.getExtras(), this);
-
- mVerb = VERB_BINDING;
- final Intent intent = new Intent().setComponent(ts.getServiceComponent());
- boolean binding = mContext.bindServiceAsUser(intent, this,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
- new UserHandle(ts.getUserId()));
- if (!binding) {
- if (DEBUG) {
- Slog.d(TAG, ts.getServiceComponent().getShortClassName() + " unavailable.");
- }
- return false;
- }
-
- return true;
- }
-
- /** Used externally to query the running task. Will return null if there is no task running. */
- TaskStatus getRunningTask() {
- return mRunningTask;
- }
-
- /** Called externally when a task that was scheduled for execution should be cancelled. */
- void cancelExecutingTask() {
- mCallbackHandler.obtainMessage(MSG_CANCEL).sendToTarget();
- }
-
- /**
- * @return Whether this context is available to handle incoming work.
- */
- boolean isAvailable() {
- synchronized (mAvailableLock) {
- return mAvailable;
- }
- }
-
- @Override
- public void taskFinished(int taskId, boolean reschedule) {
- if (!verifyCallingUid()) {
- return;
- }
- mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0)
- .sendToTarget();
- }
-
- @Override
- public void acknowledgeStopMessage(int taskId, boolean reschedule) {
- if (!verifyCallingUid()) {
- return;
- }
- mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, reschedule ? 1 : 0)
- .sendToTarget();
- }
-
- @Override
- public void acknowledgeStartMessage(int taskId, boolean ongoing) {
- if (!verifyCallingUid()) {
- return;
- }
- mCallbackHandler.obtainMessage(MSG_CALLBACK, taskId, ongoing ? 1 : 0).sendToTarget();
- }
-
- /**
- * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
- * we intend to send to the client - we stop sending work when the service is unbound so until
- * then we keep the wakelock.
- * @param name The concrete component name of the service that has been connected.
- * @param service The IBinder of the Service's communication channel,
- */
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- if (!name.equals(mRunningTask.getServiceComponent())) {
- mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
- return;
- }
- this.service = ITaskService.Stub.asInterface(service);
- // Remove all timeouts.
- mCallbackHandler.removeMessages(MSG_TIMEOUT);
- mWakeLock.acquire();
- mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
- }
-
- /**
- * If the client service crashes we reschedule this task and clean up.
- * @param name The concrete component name of the service whose
- */
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
- }
-
- /**
- * This class is reused across different clients, and passes itself in as a callback. Check
- * whether the client exercising the callback is the client we expect.
- * @return True if the binder calling is coming from the client we expect.
- */
- private boolean verifyCallingUid() {
- if (mRunningTask == null || Binder.getCallingUid() != mRunningTask.getUid()) {
- if (DEBUG) {
- Slog.d(TAG, "Stale callback received, ignoring.");
- }
- return false;
- }
- return true;
- }
-
- /**
- * Handles the lifecycle of the TaskService binding/callbacks, etc. The convention within this
- * class is to append 'H' to each function name that can only be called on this handler. This
- * isn't strictly necessary because all of these functions are private, but helps clarity.
- */
- private class TaskServiceHandler extends Handler {
- TaskServiceHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_SERVICE_BOUND:
- handleServiceBoundH();
- break;
- case MSG_CALLBACK:
- if (DEBUG) {
- Slog.d(TAG, "MSG_CALLBACK of : " + mRunningTask);
- }
- removeMessages(MSG_TIMEOUT);
-
- if (mVerb == VERB_STARTING) {
- final boolean workOngoing = message.arg2 == 1;
- handleStartedH(workOngoing);
- } else if (mVerb == VERB_EXECUTING ||
- mVerb == VERB_STOPPING) {
- final boolean reschedule = message.arg2 == 1;
- handleFinishedH(reschedule);
- } else {
- if (DEBUG) {
- Slog.d(TAG, "Unrecognised callback: " + mRunningTask);
- }
- }
- break;
- case MSG_CANCEL:
- handleCancelH();
- break;
- case MSG_TIMEOUT:
- handleOpTimeoutH();
- break;
- case MSG_SHUTDOWN_EXECUTION:
- closeAndCleanupTaskH(true /* needsReschedule */);
- default:
- Log.e(TAG, "Unrecognised message: " + message);
- }
- }
-
- /** Start the task on the service. */
- private void handleServiceBoundH() {
- if (mVerb != VERB_BINDING) {
- Slog.e(TAG, "Sending onStartTask for a task that isn't pending. "
- + VERB_STRINGS[mVerb]);
- closeAndCleanupTaskH(false /* reschedule */);
- return;
- }
- if (mCancelled.get()) {
- if (DEBUG) {
- Slog.d(TAG, "Task cancelled while waiting for bind to complete. "
- + mRunningTask);
- }
- closeAndCleanupTaskH(true /* reschedule */);
- return;
- }
- try {
- mVerb = VERB_STARTING;
- scheduleOpTimeOut();
- service.startTask(mParams);
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending onStart message to '" +
- mRunningTask.getServiceComponent().getShortClassName() + "' ", e);
- }
- }
-
- /**
- * State behaviours.
- * VERB_STARTING -> Successful start, change task to VERB_EXECUTING and post timeout.
- * _PENDING -> Error
- * _EXECUTING -> Error
- * _STOPPING -> Error
- */
- private void handleStartedH(boolean workOngoing) {
- switch (mVerb) {
- case VERB_STARTING:
- mVerb = VERB_EXECUTING;
- if (!workOngoing) {
- // Task is finished already so fast-forward to handleFinished.
- handleFinishedH(false);
- return;
- }
- if (mCancelled.get()) {
- // Cancelled *while* waiting for acknowledgeStartMessage from client.
- handleCancelH();
- return;
- }
- scheduleOpTimeOut();
- break;
- default:
- Log.e(TAG, "Handling started task but task wasn't starting! Was "
- + VERB_STRINGS[mVerb] + ".");
- return;
- }
- }
-
- /**
- * VERB_EXECUTING -> Client called taskFinished(), clean up and notify done.
- * _STOPPING -> Successful finish, clean up and notify done.
- * _STARTING -> Error
- * _PENDING -> Error
- */
- private void handleFinishedH(boolean reschedule) {
- switch (mVerb) {
- case VERB_EXECUTING:
- case VERB_STOPPING:
- closeAndCleanupTaskH(reschedule);
- break;
- default:
- Slog.e(TAG, "Got an execution complete message for a task that wasn't being" +
- "executed. Was " + VERB_STRINGS[mVerb] + ".");
- }
- }
-
- /**
- * A task can be in various states when a cancel request comes in:
- * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
- * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
- * _STARTING -> Mark as cancelled and wait for
- * {@link TaskServiceContext#acknowledgeStartMessage(int, boolean)}
- * _EXECUTING -> call {@link #sendStopMessageH}}.
- * _ENDING -> No point in doing anything here, so we ignore.
- */
- private void handleCancelH() {
- switch (mVerb) {
- case VERB_BINDING:
- case VERB_STARTING:
- mCancelled.set(true);
- break;
- case VERB_EXECUTING:
- sendStopMessageH();
- break;
- case VERB_STOPPING:
- // Nada.
- break;
- default:
- Slog.e(TAG, "Cancelling a task without a valid verb: " + mVerb);
- break;
- }
- }
-
- /** Process MSG_TIMEOUT here. */
- private void handleOpTimeoutH() {
- if (Log.isLoggable(TaskManagerService.TAG, Log.DEBUG)) {
- Log.d(TAG, "MSG_TIMEOUT of " +
- mRunningTask.getServiceComponent().getShortClassName() + " : "
- + mParams.getTaskId());
- }
-
- final int taskId = mParams.getTaskId();
- switch (mVerb) {
- case VERB_STARTING:
- // Client unresponsive - wedged or failed to respond in time. We don't really
- // know what happened so let's log it and notify the TaskManager
- // FINISHED/NO-RETRY.
- Log.e(TAG, "No response from client for onStartTask '" +
- mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
- + taskId);
- closeAndCleanupTaskH(false /* needsReschedule */);
- break;
- case VERB_STOPPING:
- // At least we got somewhere, so fail but ask the TaskManager to reschedule.
- Log.e(TAG, "No response from client for onStopTask, '" +
- mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
- + taskId);
- closeAndCleanupTaskH(true /* needsReschedule */);
- break;
- case VERB_EXECUTING:
- // Not an error - client ran out of time.
- Log.i(TAG, "Client timed out while executing (no taskFinished received)." +
- " Reporting failure and asking for reschedule. " +
- mRunningTask.getServiceComponent().getShortClassName() + "' tId: "
- + taskId);
- sendStopMessageH();
- break;
- default:
- Log.e(TAG, "Handling timeout for an unknown active task state: "
- + mRunningTask);
- return;
- }
- }
-
- /**
- * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
- * VERB_STOPPING.
- */
- private void sendStopMessageH() {
- mCallbackHandler.removeMessages(MSG_TIMEOUT);
- if (mVerb != VERB_EXECUTING) {
- Log.e(TAG, "Sending onStopTask for a task that isn't started. " + mRunningTask);
- closeAndCleanupTaskH(false /* reschedule */);
- return;
- }
- try {
- mVerb = VERB_STOPPING;
- scheduleOpTimeOut();
- service.stopTask(mParams);
- } catch (RemoteException e) {
- Log.e(TAG, "Error sending onStopTask to client.", e);
- closeAndCleanupTaskH(false);
- }
- }
-
- /**
- * The provided task has finished, either by calling
- * {@link android.app.task.TaskService#taskFinished(android.app.task.TaskParams, boolean)}
- * or from acknowledging the stop message we sent. Either way, we're done tracking it and
- * we want to clean up internally.
- */
- private void closeAndCleanupTaskH(boolean reschedule) {
- removeMessages(MSG_TIMEOUT);
- mWakeLock.release();
- mContext.unbindService(TaskServiceContext.this);
- mWakeLock = null;
-
- mRunningTask = null;
- mParams = null;
- mVerb = -1;
- mCancelled.set(false);
-
- service = null;
-
- mCompletedListener.onTaskCompleted(mRunningTask, reschedule);
- synchronized (mAvailableLock) {
- mAvailable = true;
- }
- }
-
- /**
- * Called when sending a message to the client, over whose execution we have no control. If we
- * haven't received a response in a certain amount of time, we want to give up and carry on
- * with life.
- */
- private void scheduleOpTimeOut() {
- mCallbackHandler.removeMessages(MSG_TIMEOUT);
-
- final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
- EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
- if (DEBUG) {
- Slog.d(TAG, "Scheduling time out for '" +
- mRunningTask.getServiceComponent().getShortClassName() + "' tId: " +
- mParams.getTaskId() + ", in " + (timeoutMillis / 1000) + " s");
- }
- Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
- mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
- }
- }
-}
diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java
deleted file mode 100644
index f72ab22..0000000
--- a/services/core/java/com/android/server/task/TaskStore.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task;
-
-import android.app.task.Task;
-import android.content.Context;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.server.task.controllers.TaskStatus;
-
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-/**
- * Maintain a list of classes, and accessor methods/logic for these tasks.
- * This class offers the following functionality:
- * - When a task is added, it will determine if the task requirements have changed (update) and
- * whether the controllers need to be updated.
- * - Persists Tasks, figures out when to to rewrite the Task to disk.
- * - Is threadsafe.
- * - Handles rescheduling of tasks.
- * - When a periodic task is executed and must be re-added.
- * - When a task fails and the client requests that it be retried with backoff.
- * - This class is <strong>not</strong> thread-safe.
- */
-public class TaskStore {
- private static final String TAG = "TaskManagerStore";
- /** Threshold to adjust how often we want to write to the db. */
- private static final int MAX_OPS_BEFORE_WRITE = 1;
- final ArraySet<TaskStatus> mTasks;
- final Context mContext;
-
- private int mDirtyOperations;
-
- TaskStore(Context context) {
- mTasks = intialiseTasksFromDisk();
- mContext = context;
- mDirtyOperations = 0;
- }
-
- /**
- * Add a task to the master list, persisting it if necessary. If the TaskStatus already exists,
- * it will be replaced.
- * @param taskStatus Task to add.
- * @return true if the operation succeeded.
- */
- public boolean add(TaskStatus taskStatus) {
- if (taskStatus.isPersisted()) {
- if (!maybeWriteStatusToDisk()) {
- return false;
- }
- }
- mTasks.remove(taskStatus);
- mTasks.add(taskStatus);
- return true;
- }
-
- public int size() {
- return mTasks.size();
- }
-
- /**
- * Remove the provided task. Will also delete the task if it was persisted.
- * @return The TaskStatus that was removed, or null if an invalid token was provided.
- */
- public boolean remove(TaskStatus taskStatus) {
- boolean removed = mTasks.remove(taskStatus);
- if (!removed) {
- Slog.e(TAG, "Error removing task: " + taskStatus);
- return false;
- } else {
- maybeWriteStatusToDisk();
- }
- return true;
- }
-
- /**
- * Removes all TaskStatus objects for a given uid from the master list. Note that it is
- * possible to remove a task that is pending/active. This operation will succeed, and the
- * removal will take effect when the task has completed executing.
- * @param uid Uid of the requesting app.
- * @return True if at least one task was removed, false if nothing matching the provided uId
- * was found.
- */
- public boolean removeAllByUid(int uid) {
- Iterator<TaskStatus> it = mTasks.iterator();
- boolean removed = false;
- while (it.hasNext()) {
- TaskStatus ts = it.next();
- if (ts.getUid() == uid) {
- it.remove();
- removed = true;
- }
- }
- if (removed) {
- maybeWriteStatusToDisk();
- }
- return removed;
- }
-
- /**
- * Remove the TaskStatus that matches the provided uId and taskId. Note that it is possible
- * to remove a task that is pending/active. This operation will succeed, and the removal will
- * take effect when the task has completed executing.
- * @param uid Uid of the requesting app.
- * @param taskId Task id, specified at schedule-time.
- * @return true if a removal occurred, false if the provided parameters didn't match anything.
- */
- public boolean remove(int uid, int taskId) {
- Iterator<TaskStatus> it = mTasks.iterator();
- while (it.hasNext()) {
- TaskStatus ts = it.next();
- if (ts.getUid() == uid && ts.getTaskId() == taskId) {
- it.remove();
- maybeWriteStatusToDisk();
- return true;
- }
- }
- return false;
- }
-
- /**
- * @return The live array of TaskStatus objects.
- */
- public Set<TaskStatus> getTasks() {
- return mTasks;
- }
-
- /**
- * Every time the state changes we write all the tasks in one swathe, instead of trying to
- * track incremental changes.
- * @return Whether the operation was successful. This will only fail for e.g. if the system is
- * low on storage. If this happens, we continue as normal
- */
- private boolean maybeWriteStatusToDisk() {
- mDirtyOperations++;
- if (mDirtyOperations > MAX_OPS_BEFORE_WRITE) {
- for (TaskStatus ts : mTasks) {
- //
- }
- mDirtyOperations = 0;
- }
- return true;
- }
-
- /**
- *
- * @return
- */
- // TODO: Implement this.
- private ArraySet<TaskStatus> intialiseTasksFromDisk() {
- return new ArraySet<TaskStatus>();
- }
-}
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
deleted file mode 100644
index 474af8f..0000000
--- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task.controllers;
-
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.Slog;
-
-import com.android.server.task.TaskManagerService;
-
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Handles changes in connectivity.
- * We are only interested in metered vs. unmetered networks, and we're interested in them on a
- * per-user basis.
- */
-public class ConnectivityController extends StateController {
- private static final String TAG = "TaskManager.Connectivity";
- private static final boolean DEBUG = true;
-
- private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
- private final BroadcastReceiver mConnectivityChangedReceiver =
- new ConnectivityChangedReceiver();
- /** Singleton. */
- private static ConnectivityController mSingleton;
-
- /** Track whether the latest active network is metered. */
- private boolean mMetered;
- /** Track whether the latest active network is connected. */
- private boolean mConnectivity;
-
- public static synchronized ConnectivityController get(TaskManagerService taskManager) {
- if (mSingleton == null) {
- mSingleton = new ConnectivityController(taskManager);
- }
- return mSingleton;
- }
-
- private ConnectivityController(TaskManagerService service) {
- super(service);
- // Register connectivity changed BR.
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiverAsUser(
- mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null);
- }
-
- @Override
- public synchronized void maybeStartTrackingTask(TaskStatus taskStatus) {
- if (taskStatus.hasConnectivityConstraint() || taskStatus.hasMeteredConstraint()) {
- taskStatus.connectivityConstraintSatisfied.set(mConnectivity);
- taskStatus.meteredConstraintSatisfied.set(mMetered);
- mTrackedTasks.add(taskStatus);
- }
- }
-
- @Override
- public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) {
- mTrackedTasks.remove(taskStatus);
- }
-
- /**
- * @param userId Id of the user for whom we are updating the connectivity state.
- */
- private void updateTrackedTasks(int userId) {
- boolean changed = false;
- for (TaskStatus ts : mTrackedTasks) {
- if (ts.getUserId() != userId) {
- continue;
- }
- boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity);
- boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered);
- if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) {
- changed = true;
- }
- }
- if (changed) {
- mStateChangedListener.onControllerStateChanged();
- }
- }
-
- class ConnectivityChangedReceiver extends BroadcastReceiver {
- /**
- * We'll receive connectivity changes for each user here, which we process independently.
- * We are only interested in the active network here. We're only interested in the active
- * network, b/c the end result of this will be for apps to try to hit the network.
- * @param context The Context in which the receiver is running.
- * @param intent The Intent being received.
- */
- // TODO: Test whether this will be called twice for each user.
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- final int networkType =
- intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
- ConnectivityManager.TYPE_NONE);
- // Connectivity manager for THIS context - important!
- final ConnectivityManager connManager = (ConnectivityManager)
- context.getSystemService(Context.CONNECTIVITY_SERVICE);
- final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
- // This broadcast gets sent a lot, only update if the active network has changed.
- if (activeNetwork != null && activeNetwork.getType() == networkType) {
- final int userid = context.getUserId();
- mMetered = false;
- mConnectivity =
- !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- if (mConnectivity) { // No point making the call if we know there's no conn.
- mMetered = connManager.isActiveNetworkMetered();
- }
- updateTrackedTasks(userid);
- }
- } else {
- if (DEBUG) {
- Slog.d(TAG, "Unrecognised action in intent: " + action);
- }
- }
- }
- };
-}
diff --git a/services/core/java/com/android/server/task/controllers/IdleController.java b/services/core/java/com/android/server/task/controllers/IdleController.java
deleted file mode 100644
index 9489644..0000000
--- a/services/core/java/com/android/server/task/controllers/IdleController.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task.controllers;
-
-import java.util.ArrayList;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.SystemClock;
-import android.util.Slog;
-
-import com.android.server.task.TaskManagerService;
-
-public class IdleController extends StateController {
- private static final String TAG = "IdleController";
- private static final boolean DEBUG = false;
-
- // Policy: we decide that we're "idle" if the device has been unused /
- // screen off or dreaming for at least this long
- private static final long INACTIVITY_IDLE_THRESHOLD = 71 * 60 * 1000; // millis; 71 min
- private static final long IDLE_WINDOW_SLOP = 5 * 60 * 1000; // 5 minute window, to be nice
-
- private static final String ACTION_TRIGGER_IDLE =
- "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE";
-
- final ArrayList<TaskStatus> mTrackedTasks = new ArrayList<TaskStatus>();
- IdlenessTracker mIdleTracker;
-
- // Singleton factory
- private static Object sCreationLock = new Object();
- private static volatile IdleController sController;
-
- public static IdleController get(TaskManagerService service) {
- synchronized (sCreationLock) {
- if (sController == null) {
- sController = new IdleController(service);
- }
- return sController;
- }
- }
-
- private IdleController(TaskManagerService service) {
- super(service);
- initIdleStateTracking();
- }
-
- /**
- * StateController interface
- */
- @Override
- public void maybeStartTrackingTask(TaskStatus taskStatus) {
- if (taskStatus.hasIdleConstraint()) {
- synchronized (mTrackedTasks) {
- mTrackedTasks.add(taskStatus);
- taskStatus.idleConstraintSatisfied.set(mIdleTracker.isIdle());
- }
- }
- }
-
- @Override
- public void maybeStopTrackingTask(TaskStatus taskStatus) {
- synchronized (mTrackedTasks) {
- mTrackedTasks.remove(taskStatus);
- }
- }
-
- /**
- * Interaction with the task manager service
- */
- void reportNewIdleState(boolean isIdle) {
- synchronized (mTrackedTasks) {
- for (TaskStatus task : mTrackedTasks) {
- task.idleConstraintSatisfied.set(isIdle);
- }
- }
- mStateChangedListener.onControllerStateChanged();
- }
-
- /**
- * Idle state tracking, and messaging with the task manager when
- * significant state changes occur
- */
- private void initIdleStateTracking() {
- mIdleTracker = new IdlenessTracker();
- mIdleTracker.startTracking();
- }
-
- class IdlenessTracker extends BroadcastReceiver {
- private AlarmManager mAlarm;
- private PendingIntent mIdleTriggerIntent;
- boolean mIdle;
-
- public IdlenessTracker() {
- mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-
- Intent intent = new Intent(ACTION_TRIGGER_IDLE);
- intent.setComponent(new ComponentName(mContext, this.getClass()));
- mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-
- // at boot we presume that the user has just "interacted" with the
- // device in some meaningful way
- mIdle = false;
- }
-
- public boolean isIdle() {
- return mIdle;
- }
-
- public void startTracking() {
- IntentFilter filter = new IntentFilter();
-
- // Screen state
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
-
- // Dreaming state
- filter.addAction(Intent.ACTION_DREAMING_STARTED);
- filter.addAction(Intent.ACTION_DREAMING_STOPPED);
-
- mContext.registerReceiver(this, filter);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
-
- if (action.equals(Intent.ACTION_SCREEN_ON)
- || action.equals(Intent.ACTION_DREAMING_STOPPED)) {
- // possible transition to not-idle
- if (mIdle) {
- if (DEBUG) {
- Slog.v(TAG, "exiting idle : " + action);
- }
- mAlarm.cancel(mIdleTriggerIntent);
- mIdle = false;
- reportNewIdleState(mIdle);
- }
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)
- || action.equals(Intent.ACTION_DREAMING_STARTED)) {
- // when the screen goes off or dreaming starts, we schedule the
- // alarm that will tell us when we have decided the device is
- // truly idle.
- long when = SystemClock.elapsedRealtime() + INACTIVITY_IDLE_THRESHOLD;
- if (DEBUG) {
- Slog.v(TAG, "Scheduling idle : " + action + " when=" + when);
- }
- mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- when, IDLE_WINDOW_SLOP, mIdleTriggerIntent);
- } else if (action.equals(ACTION_TRIGGER_IDLE)) {
- // idle time starts now
- if (!mIdle) {
- if (DEBUG) {
- Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime());
- }
- mIdle = true;
- reportNewIdleState(mIdle);
- }
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/task/controllers/StateController.java
deleted file mode 100644
index ed31eac..0000000
--- a/services/core/java/com/android/server/task/controllers/StateController.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task.controllers;
-
-import android.content.Context;
-
-import com.android.server.task.StateChangedListener;
-import com.android.server.task.TaskManagerService;
-
-/**
- * Incorporates shared controller logic between the various controllers of the TaskManager.
- * These are solely responsible for tracking a list of tasks, and notifying the TM when these
- * are ready to run, or whether they must be stopped.
- */
-public abstract class StateController {
-
- protected Context mContext;
- protected StateChangedListener mStateChangedListener;
-
- public StateController(TaskManagerService service) {
- mStateChangedListener = service;
- mContext = service.getContext();
- }
-
- /**
- * Implement the logic here to decide whether a task should be tracked by this controller.
- * This logic is put here so the TaskManger can be completely agnostic of Controller logic.
- * Also called when updating a task, so implementing controllers have to be aware of
- * preexisting tasks.
- */
- public abstract void maybeStartTrackingTask(TaskStatus taskStatus);
- /**
- * Remove task - this will happen if the task is cancelled, completed, etc.
- */
- public abstract void maybeStopTrackingTask(TaskStatus taskStatus);
-
-}
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
deleted file mode 100644
index b7f84ec..0000000
--- a/services/core/java/com/android/server/task/controllers/TaskStatus.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task.controllers;
-
-import android.app.task.Task;
-import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.os.UserHandle;
-
-import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Uniquely identifies a task internally.
- * Created from the public {@link android.app.task.Task} object when it lands on the scheduler.
- * Contains current state of the requirements of the task, as well as a function to evaluate
- * whether it's ready to run.
- * This object is shared among the various controllers - hence why the different fields are atomic.
- * This isn't strictly necessary because each controller is only interested in a specific field,
- * and the receivers that are listening for global state change will all run on the main looper,
- * but we don't enforce that so this is safer.
- * @hide
- */
-public class TaskStatus {
- final Task task;
- final int uId;
-
- /** At reschedule time we need to know whether to update task on disk. */
- final boolean persisted;
-
- // Constraints.
- final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
- final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
- final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
- final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
- final AtomicBoolean meteredConstraintSatisfied = new AtomicBoolean();
- final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
-
- /**
- * Earliest point in the future at which this task will be eligible to run. A value of 0
- * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
- */
- private long earliestRunTimeElapsedMillis;
- /**
- * Latest point in the future at which this task must be run. A value of {@link Long#MAX_VALUE}
- * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
- */
- private long latestRunTimeElapsedMillis;
-
- private final int numFailures;
-
- /** Provide a handle to the service that this task will be run on. */
- public int getServiceToken() {
- return uId;
- }
-
- /** Create a newly scheduled task. */
- public TaskStatus(Task task, int uId, boolean persisted) {
- this.task = task;
- this.uId = uId;
- this.numFailures = 0;
- this.persisted = persisted;
-
- final long elapsedNow = SystemClock.elapsedRealtime();
- // Timing constraints
- if (task.isPeriodic()) {
- earliestRunTimeElapsedMillis = elapsedNow;
- latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis();
- } else {
- earliestRunTimeElapsedMillis = task.hasEarlyConstraint() ?
- elapsedNow + task.getMinLatencyMillis() : 0L;
- latestRunTimeElapsedMillis = task.hasLateConstraint() ?
- elapsedNow + task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
- }
- }
-
- public TaskStatus(TaskStatus rescheduling, long newEarliestRuntimeElapsed,
- long newLatestRuntimeElapsed, int backoffAttempt) {
- this.task = rescheduling.task;
-
- this.uId = rescheduling.getUid();
- this.persisted = rescheduling.isPersisted();
- this.numFailures = backoffAttempt;
-
- earliestRunTimeElapsedMillis = newEarliestRuntimeElapsed;
- latestRunTimeElapsedMillis = newLatestRuntimeElapsed;
- }
-
- public Task getTask() {
- return task;
- }
-
- public int getTaskId() {
- return task.getId();
- }
-
- public int getNumFailures() {
- return numFailures;
- }
-
- public ComponentName getServiceComponent() {
- return task.getService();
- }
-
- public int getUserId() {
- return UserHandle.getUserId(uId);
- }
-
- public int getUid() {
- return uId;
- }
-
- public Bundle getExtras() {
- return task.getExtras();
- }
-
- public boolean hasConnectivityConstraint() {
- return task.getNetworkCapabilities() == Task.NetworkType.ANY;
- }
-
- public boolean hasMeteredConstraint() {
- return task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
- }
-
- public boolean hasChargingConstraint() {
- return task.isRequireCharging();
- }
-
- public boolean hasTimingDelayConstraint() {
- return earliestRunTimeElapsedMillis != 0L;
- }
-
- public boolean hasDeadlineConstraint() {
- return latestRunTimeElapsedMillis != Long.MAX_VALUE;
- }
-
- public boolean hasIdleConstraint() {
- return task.isRequireDeviceIdle();
- }
-
- public long getEarliestRunTime() {
- return earliestRunTimeElapsedMillis;
- }
-
- public long getLatestRunTimeElapsed() {
- return latestRunTimeElapsedMillis;
- }
-
- public boolean isPersisted() {
- return persisted;
- }
- /**
- * @return Whether or not this task is ready to run, based on its requirements.
- */
- public synchronized boolean isReady() {
- return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
- && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
- && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
- && (!hasMeteredConstraint() || meteredConstraintSatisfied.get())
- && (!hasIdleConstraint() || idleConstraintSatisfied.get())
- && (!hasDeadlineConstraint() || deadlineConstraintSatisfied.get());
- }
-
- @Override
- public int hashCode() {
- int result = getServiceComponent().hashCode();
- result = 31 * result + task.getId();
- result = 31 * result + uId;
- return result;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof TaskStatus)) return false;
-
- TaskStatus that = (TaskStatus) o;
- return ((task.getId() == that.task.getId())
- && (uId == that.uId)
- && (getServiceComponent().equals(that.getServiceComponent())));
- }
-
- // Dumpsys infrastructure
- public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("Task "); pw.println(task.getId());
- pw.print(prefix); pw.print("uid="); pw.println(uId);
- pw.print(prefix); pw.print("component="); pw.println(task.getService());
- }
-}
diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/task/controllers/TimeController.java
deleted file mode 100644
index 72f312c..0000000
--- a/services/core/java/com/android/server/task/controllers/TimeController.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.task.controllers;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.SystemClock;
-
-import com.android.server.task.TaskManagerService;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * This class sets an alarm for the next expiring task, and determines whether a task's minimum
- * delay has been satisfied.
- */
-public class TimeController extends StateController {
- private static final String TAG = "TaskManager.Time";
- private static final String ACTION_TASK_EXPIRED =
- "android.content.taskmanager.TASK_EXPIRED";
- private static final String ACTION_TASK_DELAY_EXPIRED =
- "android.content.taskmanager.TASK_DELAY_EXPIRED";
-
- /** Set an alarm for the next task expiry. */
- private final PendingIntent mTaskExpiredAlarmIntent;
- /** Set an alarm for the next task delay expiry. This*/
- private final PendingIntent mNextDelayExpiredAlarmIntent;
-
- private long mNextTaskExpiredElapsedMillis;
- private long mNextDelayExpiredElapsedMillis;
-
- private AlarmManager mAlarmService = null;
- /** List of tracked tasks, sorted asc. by deadline */
- private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
- /** Singleton. */
- private static TimeController mSingleton;
-
- public static synchronized TimeController get(TaskManagerService taskManager) {
- if (mSingleton == null) {
- mSingleton = new TimeController(taskManager);
- }
- return mSingleton;
- }
-
- private TimeController(TaskManagerService service) {
- super(service);
- mTaskExpiredAlarmIntent =
- PendingIntent.getBroadcast(mContext, 0 /* ignored */,
- new Intent(ACTION_TASK_EXPIRED), 0);
- mNextDelayExpiredAlarmIntent =
- PendingIntent.getBroadcast(mContext, 0 /* ignored */,
- new Intent(ACTION_TASK_DELAY_EXPIRED), 0);
-
- // Register BR for these intents.
- IntentFilter intentFilter = new IntentFilter(ACTION_TASK_EXPIRED);
- intentFilter.addAction(ACTION_TASK_DELAY_EXPIRED);
- mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter);
- }
-
- /**
- * Check if the task has a timing constraint, and if so determine where to insert it in our
- * list.
- */
- @Override
- public synchronized void maybeStartTrackingTask(TaskStatus task) {
- if (task.hasTimingDelayConstraint()) {
- ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size());
- while (it.hasPrevious()) {
- TaskStatus ts = it.previous();
- if (ts.equals(task)) {
- // Update
- it.remove();
- it.add(task);
- break;
- } else if (ts.getLatestRunTimeElapsed() < task.getLatestRunTimeElapsed()) {
- // Insert
- it.add(task);
- break;
- }
- }
- maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTimeElapsed());
- }
- }
-
- /**
- * If the task passed in is being tracked, figure out if we need to update our alarms, and if
- * so, update them.
- */
- @Override
- public synchronized void maybeStopTrackingTask(TaskStatus taskStatus) {
- if (mTrackedTasks.remove(taskStatus)) {
- if (mNextDelayExpiredElapsedMillis <= taskStatus.getEarliestRunTime()) {
- handleTaskDelayExpired();
- }
- if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTimeElapsed()) {
- handleTaskDeadlineExpired();
- }
- }
- }
-
- /**
- * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's
- * delay will expire.
- * This alarm <b>will not</b> wake up the phone.
- */
- private void setDelayExpiredAlarm(long alarmTimeElapsedMillis) {
- ensureAlarmService();
- mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsedMillis,
- mNextDelayExpiredAlarmIntent);
- }
-
- /**
- * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's
- * deadline will expire.
- * This alarm <b>will</b> wake up the phone.
- */
- private void setDeadlineExpiredAlarm(long alarmTimeElapsedMillis) {
- ensureAlarmService();
- mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsedMillis,
- mTaskExpiredAlarmIntent);
- }
-
- /**
- * Determines whether this controller can stop tracking the given task.
- * The controller is no longer interested in a task once its time constraint is satisfied, and
- * the task's deadline is fulfilled - unlike other controllers a time constraint can't toggle
- * back and forth.
- */
- private boolean canStopTrackingTask(TaskStatus taskStatus) {
- return (!taskStatus.hasTimingDelayConstraint() ||
- taskStatus.timeDelayConstraintSatisfied.get()) &&
- (!taskStatus.hasDeadlineConstraint() ||
- taskStatus.deadlineConstraintSatisfied.get());
- }
-
- private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
- if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
- mNextDelayExpiredElapsedMillis = delayExpiredElapsed;
- setDelayExpiredAlarm(mNextDelayExpiredElapsedMillis);
- }
- if (deadlineExpiredElapsed < mNextTaskExpiredElapsedMillis) {
- mNextTaskExpiredElapsedMillis = deadlineExpiredElapsed;
- setDeadlineExpiredAlarm(mNextTaskExpiredElapsedMillis);
- }
- }
-
- private void ensureAlarmService() {
- if (mAlarmService == null) {
- mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- }
- }
-
- /**
- * Handles alarm that notifies that a task has expired. When this function is called at least
- * one task must be run.
- */
- private synchronized void handleTaskDeadlineExpired() {
- long nextExpiryTime = Long.MAX_VALUE;
- final long nowElapsedMillis = SystemClock.elapsedRealtime();
-
- Iterator<TaskStatus> it = mTrackedTasks.iterator();
- while (it.hasNext()) {
- TaskStatus ts = it.next();
- final long taskDeadline = ts.getLatestRunTimeElapsed();
-
- if (taskDeadline <= nowElapsedMillis) {
- ts.deadlineConstraintSatisfied.set(true);
- mStateChangedListener.onTaskDeadlineExpired(ts);
- it.remove();
- } else { // Sorted by expiry time, so take the next one and stop.
- nextExpiryTime = taskDeadline;
- break;
- }
- }
- maybeUpdateAlarms(Long.MAX_VALUE, nextExpiryTime);
- }
-
- /**
- * Handles alarm that notifies us that a task's delay has expired. Iterates through the list of
- * tracked tasks and marks them as ready as appropriate.
- */
- private synchronized void handleTaskDelayExpired() {
- final long nowElapsedMillis = SystemClock.elapsedRealtime();
- long nextDelayTime = Long.MAX_VALUE;
-
- Iterator<TaskStatus> it = mTrackedTasks.iterator();
- while (it.hasNext()) {
- final TaskStatus ts = it.next();
- if (!ts.hasTimingDelayConstraint()) {
- continue;
- }
- final long taskDelayTime = ts.getEarliestRunTime();
- if (taskDelayTime < nowElapsedMillis) {
- ts.timeDelayConstraintSatisfied.set(true);
- if (canStopTrackingTask(ts)) {
- it.remove();
- }
- } else { // Keep going through list to get next delay time.
- if (nextDelayTime > taskDelayTime) {
- nextDelayTime = taskDelayTime;
- }
- }
- }
- mStateChangedListener.onControllerStateChanged();
- maybeUpdateAlarms(nextDelayTime, Long.MAX_VALUE);
- }
-
- private final BroadcastReceiver mAlarmExpiredReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- // An task has just expired, so we run through the list of tasks that we have and
- // notify our StateChangedListener.
- if (ACTION_TASK_EXPIRED.equals(intent.getAction())) {
- handleTaskDeadlineExpired();
- } else if (ACTION_TASK_DELAY_EXPIRED.equals(intent.getAction())) {
- handleTaskDelayExpired();
- }
- }
- };
-}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 1629a614..b9ef492 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -50,6 +50,7 @@
import android.service.trust.TrustAgentService;
import android.util.ArraySet;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.Xml;
@@ -81,6 +82,7 @@
private static final Intent TRUST_AGENT_INTENT =
new Intent(TrustAgentService.SERVICE_INTERFACE);
+ private static final String PERMISSION_PROVIDE_AGENT = Manifest.permission.PROVIDE_TRUST_AGENT;
private static final int MSG_REGISTER_LISTENER = 1;
private static final int MSG_UNREGISTER_LISTENER = 2;
@@ -182,6 +184,19 @@
PackageManager.GET_META_DATA, userInfo.id);
for (ResolveInfo resolveInfo : resolveInfos) {
if (resolveInfo.serviceInfo == null) continue;
+
+ String packageName = resolveInfo.serviceInfo.packageName;
+ // STOPSHIP Reenable this check once the GMS Core prebuild library has the
+ // permission.
+ /*
+ if (pm.checkPermission(PERMISSION_PROVIDE_AGENT, packageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "Skipping agent because package " + packageName
+ + " does not have permission " + PERMISSION_PROVIDE_AGENT + ".");
+ continue;
+ }
+ */
+
ComponentName name = getComponentName(resolveInfo);
if (!enabledAgents.contains(name)) continue;
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index 4bdd2be..34168a8 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -16,10 +16,10 @@
package com.android.server.tv;
+import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvStreamConfig;
import android.os.Handler;
import android.os.HandlerThread;
-import android.tv.TvInputHardwareInfo;
-import android.tv.TvStreamConfig;
import android.view.Surface;
/**
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index b95b0f0..e34f42b 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -17,12 +17,12 @@
package com.android.server.tv;
import android.content.Context;
+import android.media.tv.ITvInputHardware;
+import android.media.tv.ITvInputHardwareCallback;
+import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvStreamConfig;
import android.os.IBinder;
import android.os.RemoteException;
-import android.tv.ITvInputHardware;
-import android.tv.ITvInputHardwareCallback;
-import android.tv.TvInputHardwareInfo;
-import android.tv.TvStreamConfig;
import android.util.Slog;
import android.util.SparseArray;
import android.view.KeyEvent;
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e52f218..10a67c4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -31,6 +31,18 @@
import android.content.pm.ServiceInfo;
import android.database.Cursor;
import android.graphics.Rect;
+import android.media.tv.ITvInputClient;
+import android.media.tv.ITvInputHardware;
+import android.media.tv.ITvInputHardwareCallback;
+import android.media.tv.ITvInputManager;
+import android.media.tv.ITvInputService;
+import android.media.tv.ITvInputServiceCallback;
+import android.media.tv.ITvInputSession;
+import android.media.tv.ITvInputSessionCallback;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputService;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
@@ -41,18 +53,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.TvContract;
-import android.tv.ITvInputClient;
-import android.tv.ITvInputHardware;
-import android.tv.ITvInputHardwareCallback;
-import android.tv.ITvInputManager;
-import android.tv.ITvInputService;
-import android.tv.ITvInputServiceCallback;
-import android.tv.ITvInputSession;
-import android.tv.ITvInputSessionCallback;
-import android.tv.TvInputHardwareInfo;
-import android.tv.TvInputInfo;
-import android.tv.TvInputService;
import android.util.Slog;
import android.util.SparseArray;
import android.view.InputChannel;
@@ -63,6 +63,10 @@
import com.android.server.IoThread;
import com.android.server.SystemService;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -145,7 +149,8 @@
if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServices(
- new Intent(TvInputService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
+ new Intent(TvInputService.SERVICE_INTERFACE),
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
for (ResolveInfo ri : services) {
ServiceInfo si = ri.serviceInfo;
if (!android.Manifest.permission.BIND_TV_INPUT.equals(si.permission)) {
@@ -153,9 +158,13 @@
+ android.Manifest.permission.BIND_TV_INPUT);
continue;
}
- TvInputInfo info = new TvInputInfo(ri);
- if (DEBUG) Slog.d(TAG, "add " + info.getId());
- userState.inputMap.put(info.getId(), info);
+ try {
+ TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
+ if (DEBUG) Slog.d(TAG, "add " + info.getId());
+ userState.inputMap.put(info.getId(), info);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "Can't load TV input " + si.name, e);
+ }
}
}
@@ -204,11 +213,13 @@
Slog.e(TAG, "error in unregisterCallback", e);
}
}
- serviceState.mClients.clear();
+ serviceState.mClientTokens.clear();
mContext.unbindService(serviceState.mConnection);
}
userState.serviceStateMap.clear();
+ userState.clientStateMap.clear();
+
mUserStates.remove(userId);
}
}
@@ -273,7 +284,7 @@
}
serviceState.mReconnecting = false;
}
- boolean isStateEmpty = serviceState.mClients.isEmpty()
+ boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
&& serviceState.mSessionTokens.isEmpty();
if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
// This means that the service is not yet connected but its state indicates that we
@@ -290,9 +301,8 @@
Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
userState.inputMap.get(inputId).getComponent());
- mContext.bindServiceAsUser(i, serviceState.mConnection, Context.BIND_AUTO_CREATE,
- new UserHandle(userId));
- serviceState.mBound = true;
+ serviceState.mBound = mContext.bindServiceAsUser(
+ i, serviceState.mConnection, Context.BIND_AUTO_CREATE, new UserHandle(userId));
} else if (serviceState.mService != null && isStateEmpty) {
// This means that the service is already connected but its state indicates that we have
// nothing to do with it. Then, disconnect the service.
@@ -304,10 +314,22 @@
}
}
+ private ClientState createClientStateLocked(IBinder clientToken, int userId) {
+ UserState userState = getUserStateLocked(userId);
+ ClientState clientState = new ClientState(clientToken, userId);
+ try {
+ clientToken.linkToDeath(clientState, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Client is already died.");
+ }
+ userState.clientStateMap.put(clientToken, clientState);
+ return clientState;
+ }
+
private void createSessionInternalLocked(ITvInputService service, final IBinder sessionToken,
final int userId) {
- final SessionState sessionState =
- getUserStateLocked(userId).sessionStateMap.get(sessionToken);
+ final UserState userState = getUserStateLocked(userId);
+ final SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (DEBUG) {
Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
}
@@ -333,6 +355,14 @@
} catch (RemoteException e) {
Slog.e(TAG, "Session is already died.");
}
+
+ IBinder clientToken = sessionState.mClient.asBinder();
+ ClientState clientState = userState.clientStateMap.get(clientToken);
+ if (clientState == null) {
+ clientState = createClientStateLocked(clientToken, userId);
+ }
+ clientState.mSessionTokens.add(sessionState.mSessionToken);
+
sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
sessionToken, channels[0], sessionState.mSeq, userId);
}
@@ -341,18 +371,54 @@
}
@Override
- public void onVideoSizeChanged(int width, int height) throws RemoteException {
+ public void onVideoStreamChanged(int width, int height, boolean interlaced) {
synchronized (mLock) {
if (DEBUG) {
- Slog.d(TAG, "onVideoSizeChanged(" + width + ", " + height + ")");
+ Slog.d(TAG, "onVideoStreamChanged(" + width + ", " + height + ")");
}
if (sessionState.mSession == null || sessionState.mClient == null) {
return;
}
try {
- sessionState.mClient.onVideoSizeChanged(width, height, sessionState.mSeq);
+ sessionState.mClient.onVideoStreamChanged(width, height, interlaced,
+ sessionState.mSeq);
} catch (RemoteException e) {
- Slog.e(TAG, "error in onSessionEvent");
+ Slog.e(TAG, "error in onVideoStreamChanged");
+ }
+ }
+ }
+
+ @Override
+ public void onAudioStreamChanged(int channelCount) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAudioStreamChanged(" + channelCount + ")");
+ }
+ if (sessionState.mSession == null || sessionState.mClient == null) {
+ return;
+ }
+ try {
+ sessionState.mClient.onAudioStreamChanged(channelCount, sessionState.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAudioStreamChanged");
+ }
+ }
+ }
+
+ @Override
+ public void onClosedCaptionStreamChanged(boolean hasClosedCaption) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onClosedCaptionStreamChanged(" + hasClosedCaption + ")");
+ }
+ if (sessionState.mSession == null || sessionState.mClient == null) {
+ return;
+ }
+ try {
+ sessionState.mClient.onClosedCaptionStreamChanged(hasClosedCaption,
+ sessionState.mSeq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onClosedCaptionStreamChanged");
}
}
}
@@ -423,7 +489,16 @@
mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
}
- // Also remove the session token from the session token list of the current service.
+ // Also remove the session token from the session token list of the current client and
+ // service.
+ ClientState clientState = userState.clientStateMap.get(sessionState.mClient.asBinder());
+ if (clientState != null) {
+ clientState.mSessionTokens.remove(sessionToken);
+ if (clientState.isEmpty()) {
+ userState.clientStateMap.remove(sessionState.mClient.asBinder());
+ }
+ }
+
ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
if (serviceState != null) {
serviceState.mSessionTokens.remove(sessionToken);
@@ -431,11 +506,45 @@
updateServiceConnectionLocked(sessionState.mInputId, userId);
}
+ private void unregisterCallbackInternalLocked(IBinder clientToken, String inputId,
+ int userId) {
+ UserState userState = getUserStateLocked(userId);
+ ClientState clientState = userState.clientStateMap.get(clientToken);
+ if (clientState != null) {
+ clientState.mInputIds.remove(inputId);
+ if (clientState.isEmpty()) {
+ userState.clientStateMap.remove(clientToken);
+ }
+ }
+
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
+ if (serviceState == null) {
+ return;
+ }
+
+ // Remove this client from the client list and unregister the callback.
+ serviceState.mClientTokens.remove(clientToken);
+ if (!serviceState.mClientTokens.isEmpty()) {
+ // We have other clients who want to keep the callback. Do this later.
+ return;
+ }
+ if (serviceState.mService == null || serviceState.mCallback == null) {
+ return;
+ }
+ try {
+ serviceState.mService.unregisterCallback(serviceState.mCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ } finally {
+ serviceState.mCallback = null;
+ updateServiceConnectionLocked(inputId, userId);
+ }
+ }
+
private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
- for (IBinder iBinder : serviceState.mClients) {
- ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
+ for (IBinder clientToken : serviceState.mClientTokens) {
try {
- client.onAvailabilityChanged(
+ ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged(
serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
} catch (RemoteException e) {
Slog.e(TAG, "error in onAvailabilityChanged", e);
@@ -498,10 +607,19 @@
userState.inputMap.get(inputId), resolvedUserId);
userState.serviceStateMap.put(inputId, serviceState);
}
- IBinder iBinder = client.asBinder();
- if (!serviceState.mClients.contains(iBinder)) {
- serviceState.mClients.add(iBinder);
+ IBinder clientToken = client.asBinder();
+ if (!serviceState.mClientTokens.contains(clientToken)) {
+ serviceState.mClientTokens.add(clientToken);
}
+
+ ClientState clientState = userState.clientStateMap.get(clientToken);
+ if (clientState == null) {
+ clientState = createClientStateLocked(clientToken, resolvedUserId);
+ }
+ if (!clientState.mInputIds.contains(inputId)) {
+ clientState.mInputIds.add(inputId);
+ }
+
if (serviceState.mService != null) {
if (serviceState.mCallback != null) {
// We already handled.
@@ -529,29 +647,7 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- UserState userState = getUserStateLocked(resolvedUserId);
- ServiceState serviceState = userState.serviceStateMap.get(inputId);
- if (serviceState == null) {
- return;
- }
-
- // Remove this client from the client list and unregister the callback.
- serviceState.mClients.remove(client.asBinder());
- if (!serviceState.mClients.isEmpty()) {
- // We have other clients who want to keep the callback. Do this later.
- return;
- }
- if (serviceState.mService == null || serviceState.mCallback == null) {
- return;
- }
- try {
- serviceState.mService.unregisterCallback(serviceState.mCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in unregisterCallback", e);
- } finally {
- serviceState.mCallback = null;
- updateServiceConnectionLocked(inputId, resolvedUserId);
- }
+ unregisterCallbackInternalLocked(client.asBinder(), inputId, resolvedUserId);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -582,8 +678,8 @@
// Create a new session token and a session state.
IBinder sessionToken = new Binder();
- SessionState sessionState = new SessionState(
- sessionToken, inputId, client, seq, callingUid, resolvedUserId);
+ SessionState sessionState = new SessionState(sessionToken, inputId, client,
+ seq, callingUid, resolvedUserId);
// Add them to the global session state map of the current user.
userState.sessionStateMap.put(sessionToken, sessionState);
@@ -835,6 +931,10 @@
// A mapping from the TV input id to its TvInputInfo.
private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
+ // A mapping from the token of a client to its state.
+ private final Map<IBinder, ClientState> clientStateMap =
+ new HashMap<IBinder, ClientState>();
+
// A mapping from the name of a TV input service to its state.
private final Map<String, ServiceState> serviceStateMap =
new HashMap<String, ServiceState>();
@@ -844,9 +944,46 @@
new HashMap<IBinder, SessionState>();
}
+ private final class ClientState implements IBinder.DeathRecipient {
+ private final List<String> mInputIds = new ArrayList<String>();
+ private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
+
+ private IBinder mClientToken;
+ private final int mUserId;
+
+ ClientState(IBinder clientToken, int userId) {
+ mClientToken = clientToken;
+ mUserId = userId;
+ }
+
+ public boolean isEmpty() {
+ return mInputIds.isEmpty() && mSessionTokens.isEmpty();
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ UserState userState = getUserStateLocked(mUserId);
+ // DO NOT remove the client state of clientStateMap in this method. It will be
+ // removed in releaseSessionLocked() or unregisterCallbackInternalLocked().
+ ClientState clientState = userState.clientStateMap.get(mClientToken);
+ if (clientState != null) {
+ while (clientState.mSessionTokens.size() > 0) {
+ releaseSessionLocked(
+ clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
+ }
+ while (clientState.mInputIds.size() > 0) {
+ unregisterCallbackInternalLocked(
+ mClientToken, clientState.mInputIds.get(0), mUserId);
+ }
+ }
+ mClientToken = null;
+ }
+ }
+ }
+
private final class ServiceState {
- // TODO: need to implement DeathRecipient for clients.
- private final List<IBinder> mClients = new ArrayList<IBinder>();
+ private final List<IBinder> mClientTokens = new ArrayList<IBinder>();
private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
private final ServiceConnection mConnection;
private final TvInputInfo mTvInputInfo;
@@ -869,13 +1006,13 @@
private final int mSeq;
private final int mCallingUid;
private final int mUserId;
- private final IBinder mToken;
+ private final IBinder mSessionToken;
private ITvInputSession mSession;
private Uri mLogUri;
- private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
+ private SessionState(IBinder sessionToken, String inputId, ITvInputClient client, int seq,
int callingUid, int userId) {
- mToken = token;
+ mSessionToken = sessionToken;
mInputId = inputId;
mClient = client;
mSeq = seq;
@@ -894,7 +1031,7 @@
Slog.e(TAG, "error in onSessionReleased", e);
}
}
- removeSessionStateLocked(mToken, mUserId);
+ removeSessionStateLocked(mSessionToken, mUserId);
}
}
}
@@ -918,7 +1055,7 @@
serviceState.mService = ITvInputService.Stub.asInterface(service);
// Register a callback, if we need to.
- if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) {
+ if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) {
serviceState.mCallback = new ServiceCallback(mUserId);
try {
serviceState.mService.registerCallback(serviceState.mCallback);
@@ -1046,7 +1183,7 @@
TvContract.Programs.COLUMN_TITLE,
TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
- TvContract.Programs.COLUMN_DESCRIPTION
+ TvContract.Programs.COLUMN_SHORT_DESCRIPTION
};
String selection = TvContract.Programs.COLUMN_CHANNEL_ID + "=? AND "
+ TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS + "<=? AND "
diff --git a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index 55dd4ab..24318df 100644
--- a/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -42,6 +42,7 @@
private static final String seappContextsPath = "seapp_contexts";
private static final String versionPath = "selinux_version";
private static final String macPermissionsPath = "mac_permissions.xml";
+ private static final String serviceContextsPath = "service_contexts";
public SELinuxPolicyInstallReceiver() {
super("/data/security/bundle", "sepolicy_bundle", "metadata/", "version");
@@ -65,6 +66,9 @@
new File(contexts, sepolicyPath).renameTo(
new File(contexts, sepolicyPath + "_backup"));
+
+ new File(contexts, serviceContextsPath).renameTo(
+ new File(contexts, serviceContextsPath + "_backup"));
}
private void copyUpdate(File contexts) {
@@ -74,6 +78,7 @@
new File(updateDir, propertyContextsPath).renameTo(new File(contexts, propertyContextsPath));
new File(updateDir, fileContextsPath).renameTo(new File(contexts, fileContextsPath));
new File(updateDir, sepolicyPath).renameTo(new File(contexts, sepolicyPath));
+ new File(updateDir, serviceContextsPath).renameTo(new File(contexts, serviceContextsPath));
}
private int readInt(BufferedInputStream reader) throws IOException {
@@ -85,13 +90,14 @@
}
private int[] readChunkLengths(BufferedInputStream bundle) throws IOException {
- int[] chunks = new int[6];
+ int[] chunks = new int[7];
chunks[0] = readInt(bundle);
chunks[1] = readInt(bundle);
chunks[2] = readInt(bundle);
chunks[3] = readInt(bundle);
chunks[4] = readInt(bundle);
chunks[5] = readInt(bundle);
+ chunks[6] = readInt(bundle);
return chunks;
}
@@ -112,6 +118,7 @@
installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[3]);
installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[4]);
installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[5]);
+ installFile(new File(updateDir, serviceContextsPath), stream, chunkLengths[6]);
} finally {
IoUtils.closeQuietly(stream);
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 35b7f99..6cb6b76 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -44,6 +44,7 @@
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
+import android.view.ViewConfiguration;
import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.WindowManagerInternal.MagnificationCallbacks;
@@ -113,13 +114,13 @@
mDisplayMagnifier.setMagnificationSpecLocked(spec);
}
if (mWindowsForAccessibilityObserver != null) {
- mWindowsForAccessibilityObserver.computeChangedWindows();
+ mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
- public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+ public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
if (mDisplayMagnifier != null) {
- mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
+ mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
}
// Not relevant for the window observer.
}
@@ -129,7 +130,7 @@
mDisplayMagnifier.onWindowLayersChangedLocked();
}
if (mWindowsForAccessibilityObserver != null) {
- mWindowsForAccessibilityObserver.computeChangedWindows();
+ mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
@@ -138,7 +139,7 @@
mDisplayMagnifier.onRotationChangedLocked(displayContent, rotation);
}
if (mWindowsForAccessibilityObserver != null) {
- mWindowsForAccessibilityObserver.computeChangedWindows();
+ mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
@@ -154,7 +155,7 @@
mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
}
if (mWindowsForAccessibilityObserver != null) {
- mWindowsForAccessibilityObserver.computeChangedWindows();
+ mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
@@ -162,7 +163,16 @@
// Not relevant for the display magnifier.
if (mWindowsForAccessibilityObserver != null) {
- mWindowsForAccessibilityObserver.computeChangedWindows();
+ mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
+ }
+ }
+
+
+ public void onSomeWindowResizedOrMovedLocked() {
+ // Not relevant for the display magnifier.
+
+ if (mWindowsForAccessibilityObserver != null) {
+ mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
}
}
@@ -247,7 +257,7 @@
mWindowManagerService.scheduleAnimationLocked();
}
- public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+ public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
if (DEBUG_RECTANGLE_REQUESTED) {
Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
}
@@ -541,7 +551,7 @@
if (isMagnifyingLocked()) {
setMagnifiedRegionBorderShownLocked(false, false);
final long delay = (long) (mLongAnimationDuration
- * mWindowManagerService.mWindowAnimationScale);
+ * mWindowManagerService.getWindowAnimationScaleLocked());
Message message = mHandler.obtainMessage(
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
mHandler.sendMessageDelayed(message, delay);
@@ -880,21 +890,45 @@
private final WindowsForAccessibilityCallback mCallback;
+ private final long mRecurringAccessibilityEventsIntervalMillis;
+
public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
WindowsForAccessibilityCallback callback) {
mContext = windowManagerService.mContext;
mWindowManagerService = windowManagerService;
mCallback = callback;
mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
+ mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
+ .getSendRecurringAccessibilityEventsInterval();
computeChangedWindows();
}
+ public void scheduleComputeChangedWindowsLocked() {
+ // If focus changed, compute changed windows immediately as the focused window
+ // is used by the accessibility manager service to determine the active window.
+ if (mWindowManagerService.mCurrentFocus != null
+ && mWindowManagerService.mCurrentFocus != mWindowManagerService.mLastFocus) {
+ mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
+ computeChangedWindows();
+ } else if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
+ mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
+ mRecurringAccessibilityEventsIntervalMillis);
+ }
+ }
+
public void computeChangedWindows() {
if (DEBUG) {
Slog.i(LOG_TAG, "computeChangedWindows()");
}
synchronized (mWindowManagerService.mWindowMap) {
+ // Do not send the windows if there is no current focus as
+ // the window manager is still looking for where to put it.
+ // We will do the work when we get a focus change callback.
+ if (mWindowManagerService.mCurrentFocus == null) {
+ return;
+ }
+
WindowManager windowManager = (WindowManager)
mContext.getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getRealSize(mTempPoint);
@@ -912,37 +946,21 @@
Set<IBinder> addedWindows = mTempBinderSet;
addedWindows.clear();
+ boolean focusedWindowAdded = false;
+
final int visibleWindowCount = visibleWindows.size();
for (int i = visibleWindowCount - 1; i >= 0; i--) {
WindowState windowState = visibleWindows.valueAt(i);
- // Compute the window touchable frame as shown on the screen.
- // Get the touchable frame.
- Region touchableRegion = mTempRegion1;
- windowState.getTouchableRegion(touchableRegion);
- Rect touchableFrame = mTempRect;
- touchableRegion.getBounds(touchableFrame);
-
- // Move to origin as all transforms are captured by the matrix.
- RectF windowFrame = mTempRectF;
- windowFrame.set(touchableFrame);
- windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
-
- // Map the frame to get what appears on the screen.
- Matrix matrix = mTempMatrix;
- populateTransformationMatrixLocked(windowState, matrix);
- matrix.mapRect(windowFrame);
-
- // Got the bounds.
+ // Compute the bounds in the screen.
Rect boundsInScreen = mTempRect;
- boundsInScreen.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
+ computeWindowBoundsInScreen(windowState, boundsInScreen);
final int flags = windowState.mAttrs.flags;
// If the window is not touchable, do not report it but take into account
// the space it takes since the content behind it cannot be touched.
- if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 1) {
+ if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
unaccountedSpace.op(boundsInScreen, unaccountedSpace,
Region.Op.DIFFERENCE);
continue;
@@ -956,33 +974,12 @@
// Add windows of certain types not covered by modal windows.
if (isReportedWindowType(windowState.mAttrs.type)) {
// Add the window to the ones to be reported.
- WindowInfo window = WindowInfo.obtain();
- window.type = windowState.mAttrs.type;
- window.layer = windowState.mLayer;
- window.token = windowState.mClient.asBinder();
-
+ WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
addedWindows.add(window.token);
-
- WindowState attachedWindow = windowState.mAttachedWindow;
- if (attachedWindow != null) {
- window.parentToken = attachedWindow.mClient.asBinder();
- }
-
- window.focused = windowState.isFocused();
- window.boundsInScreen.set(boundsInScreen);
-
- final int childCount = windowState.mChildWindows.size();
- if (childCount > 0) {
- if (window.childTokens == null) {
- window.childTokens = new ArrayList<IBinder>();
- }
- for (int j = 0; j < childCount; j++) {
- WindowState child = windowState.mChildWindows.get(j);
- window.childTokens.add(child.mClient.asBinder());
- }
- }
-
windows.add(window);
+ if (windowState.isFocused()) {
+ focusedWindowAdded = true;
+ }
}
// Account for the space this window takes.
@@ -1001,6 +998,25 @@
}
}
+ // Always report the focused window.
+ if (!focusedWindowAdded) {
+ for (int i = visibleWindowCount - 1; i >= 0; i--) {
+ WindowState windowState = visibleWindows.valueAt(i);
+ if (windowState.isFocused()) {
+ // Compute the bounds in the screen.
+ Rect boundsInScreen = mTempRect;
+ computeWindowBoundsInScreen(windowState, boundsInScreen);
+
+ // Add the window to the ones to be reported.
+ WindowInfo window = obtainPopulatedWindowInfo(windowState,
+ boundsInScreen);
+ addedWindows.add(window.token);
+ windows.add(window);
+ break;
+ }
+ }
+ }
+
// Remove child/parent references to windows that were not added.
final int windowCount = windows.size();
for (int i = 0; i < windowCount; i++) {
@@ -1063,6 +1079,57 @@
}
}
+ private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
+ // Get the touchable frame.
+ Region touchableRegion = mTempRegion1;
+ windowState.getTouchableRegion(touchableRegion);
+ Rect touchableFrame = mTempRect;
+ touchableRegion.getBounds(touchableFrame);
+
+ // Move to origin as all transforms are captured by the matrix.
+ RectF windowFrame = mTempRectF;
+ windowFrame.set(touchableFrame);
+ windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+
+ // Map the frame to get what appears on the screen.
+ Matrix matrix = mTempMatrix;
+ populateTransformationMatrixLocked(windowState, matrix);
+ matrix.mapRect(windowFrame);
+
+ // Got the bounds.
+ outBounds.set((int) windowFrame.left, (int) windowFrame.top,
+ (int) windowFrame.right, (int) windowFrame.bottom);
+ }
+
+ private static WindowInfo obtainPopulatedWindowInfo(WindowState windowState,
+ Rect boundsInScreen) {
+ WindowInfo window = WindowInfo.obtain();
+ window.type = windowState.mAttrs.type;
+ window.layer = windowState.mLayer;
+ window.token = windowState.mClient.asBinder();
+
+ WindowState attachedWindow = windowState.mAttachedWindow;
+ if (attachedWindow != null) {
+ window.parentToken = attachedWindow.mClient.asBinder();
+ }
+
+ window.focused = windowState.isFocused();
+ window.boundsInScreen.set(boundsInScreen);
+
+ final int childCount = windowState.mChildWindows.size();
+ if (childCount > 0) {
+ if (window.childTokens == null) {
+ window.childTokens = new ArrayList<IBinder>();
+ }
+ for (int j = 0; j < childCount; j++) {
+ WindowState child = windowState.mChildWindows.get(j);
+ window.childTokens.add(child.mClient.asBinder());
+ }
+ }
+
+ return window;
+ }
+
private void cacheWindows(List<WindowInfo> windows) {
final int oldWindowCount = mOldWindows.size();
for (int i = oldWindowCount - 1; i >= 0; i--) {
@@ -1079,10 +1146,10 @@
if (oldWindow == newWindow) {
return false;
}
- if (oldWindow == null && newWindow != null) {
+ if (oldWindow == null) {
return true;
}
- if (oldWindow != null && newWindow == null) {
+ if (newWindow == null) {
return true;
}
if (oldWindow.type != newWindow.type) {
@@ -1151,7 +1218,8 @@
}
private class MyHandler extends Handler {
- public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1;
+ public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
+ public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 2;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -1161,6 +1229,10 @@
@SuppressWarnings("unchecked")
public void handleMessage(Message message) {
switch (message.what) {
+ case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
+ computeChangedWindows();
+ } break;
+
case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
List<WindowInfo> windows = (List<WindowInfo>) message.obj;
mCallback.onWindowsForAccessibilityChanged(windows);
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 7fe895b..63ae98e 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -87,7 +87,7 @@
anim.initialize(width, height, width, height);
}
anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(mService.mTransitionAnimationScale);
+ anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
int zorder = anim.getZAdjustment();
int adj = 0;
if (zorder == Animation.ZORDER_TOP) {
@@ -227,7 +227,8 @@
if (!animating) {
if (WindowManagerService.DEBUG_ANIM) Slog.v(
TAG, "Starting animation in " + mAppToken +
- " @ " + currentTime + " scale=" + mService.mTransitionAnimationScale
+ " @ " + currentTime + " scale="
+ + mService.getTransitionAnimationScaleLocked()
+ " allDrawn=" + mAppToken.allDrawn + " animating=" + animating);
animation.setStartTime(currentTime);
animating = true;
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
deleted file mode 100644
index 35d19c1..0000000
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.view.Display;
-import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-
-class CircularDisplayMask {
- private static final String TAG = "CircularDisplayMask";
-
- private static final int STROKE_WIDTH = 2;
-
- private final SurfaceControl mSurfaceControl;
- private final Surface mSurface = new Surface();
- private int mLastDW;
- private int mLastDH;
- private boolean mDrawNeeded;
- private Paint mPaint;
- private int mRotation;
-
- public CircularDisplayMask(Display display, SurfaceSession session, int zOrder) {
- SurfaceControl ctrl = null;
- try {
- ctrl = new SurfaceControl(session, "CircularDisplayMask",
- 320, 290, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
- ctrl.setLayerStack(display.getLayerStack());
- ctrl.setLayer(zOrder);
- ctrl.setPosition(0, 0);
- ctrl.show();
- mSurface.copyFrom(ctrl);
- } catch (OutOfResourcesException e) {
- }
- mSurfaceControl = ctrl;
- mDrawNeeded = true;
- mPaint = new Paint();
- mPaint.setAntiAlias(true);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setColor(Color.BLACK);
- mPaint.setStrokeWidth(STROKE_WIDTH);
- }
-
- private void drawIfNeeded() {
- if (!mDrawNeeded) {
- return;
- }
- mDrawNeeded = false;
-
- Rect dirty = new Rect(0, 0, mLastDW, mLastDH);
- Canvas c = null;
- try {
- c = mSurface.lockCanvas(dirty);
- } catch (IllegalArgumentException e) {
- } catch (Surface.OutOfResourcesException e) {
- }
- if (c == null) {
- return;
- }
- int cx = 160;
- int cy = 160;
- switch (mRotation) {
- case Surface.ROTATION_0:
- case Surface.ROTATION_90:
- // chin bottom or right
- cx = 160;
- cy = 160;
- break;
- case Surface.ROTATION_180:
- // chin top
- cx = 160;
- cy = 145;
- break;
- case Surface.ROTATION_270:
- cx = 145;
- cy = 160;
- break;
- }
- c.drawCircle(cx, cy, 160, mPaint);
-
- mSurface.unlockCanvasAndPost(c);
- }
-
- // Note: caller responsible for being inside
- // Surface.openTransaction() / closeTransaction()
- public void setVisibility(boolean on) {
- if (mSurfaceControl == null) {
- return;
- }
- drawIfNeeded();
- if (on) {
- mSurfaceControl.show();
- } else {
- mSurfaceControl.hide();
- }
- }
-
- void positionSurface(int dw, int dh, int rotation) {
- if (mLastDW == dw && mLastDH == dh) {
- return;
- }
- mLastDW = dw;
- mLastDH = dh;
- mSurfaceControl.setSize(dw, dh);
- mDrawNeeded = true;
- mRotation = rotation;
- }
-
-}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ca9076f..b4cf2ae 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.view.IWindowId;
+import android.view.IWindowSessionCallback;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
@@ -54,6 +55,7 @@
final class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
final WindowManagerService mService;
+ final IWindowSessionCallback mCallback;
final IInputMethodClient mClient;
final IInputContext mInputContext;
final int mUid;
@@ -62,14 +64,17 @@
SurfaceSession mSurfaceSession;
int mNumWindow = 0;
boolean mClientDead = false;
+ float mLastReportedAnimatorScale;
- public Session(WindowManagerService service, IInputMethodClient client,
- IInputContext inputContext) {
+ public Session(WindowManagerService service, IWindowSessionCallback callback,
+ IInputMethodClient client, IInputContext inputContext) {
mService = service;
+ mCallback = callback;
mClient = client;
mInputContext = inputContext;
mUid = Binder.getCallingUid();
mPid = Binder.getCallingPid();
+ mLastReportedAnimatorScale = service.getCurrentAnimatorScale();
StringBuilder sb = new StringBuilder();
sb.append("Session{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
@@ -441,11 +446,11 @@
}
}
- public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
+ public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
synchronized(mService.mWindowMap) {
final long identity = Binder.clearCallingIdentity();
try {
- mService.onRectangleOnScreenRequested(token, rectangle, immediate);
+ mService.onRectangleOnScreenRequested(token, rectangle);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -464,6 +469,9 @@
if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(
WindowManagerService.TAG, " NEW SURFACE SESSION " + mSurfaceSession);
mService.mSessions.add(this);
+ if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
+ mService.dispatchNewAnimatorScaleLocked(this);
+ }
}
mNumWindow++;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 008d2fc..45326f7 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED;
@@ -227,7 +228,7 @@
continue;
}
final WindowStateAnimator winAnimator = win.mWinAnimator;
- if (mPolicy.doesForceHide(win.mAttrs)) {
+ if ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
if (!winAnimator.mAnimating) {
// Create a new animation to delay until keyguard is gone on its own.
winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
@@ -268,7 +269,7 @@
}
}
- if (mPolicy.doesForceHide(win.mAttrs)) {
+ if (mPolicy.isForceHiding(win.mAttrs)) {
if (!wasAnimating && nowAnimating) {
if (WindowManagerService.DEBUG_ANIM ||
WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 05502cf..4eea9c3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -24,6 +24,7 @@
import android.util.TimeUtils;
import android.view.IWindowId;
+import android.view.IWindowSessionCallback;
import android.view.WindowContentFrameStats;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
@@ -150,7 +151,6 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -339,7 +339,7 @@
/**
* All currently active sessions with clients.
*/
- final HashSet<Session> mSessions = new HashSet<Session>();
+ final ArraySet<Session> mSessions = new ArraySet<Session>();
/**
* Mapping from an IWindow IBinder to the server's Window object.
@@ -407,8 +407,11 @@
/**
* Windows that clients are waiting to have drawn.
*/
- ArrayList<Pair<WindowState, IRemoteCallback>> mWaitingForDrawn
- = new ArrayList<Pair<WindowState, IRemoteCallback>>();
+ ArrayList<WindowState> mWaitingForDrawn = new ArrayList<WindowState>();
+ /**
+ * And the callback to make when they've all been drawn.
+ */
+ IRemoteCallback mWaitingForDrawnCallback;
/**
* Windows that have called relayout() while we were running animations,
@@ -429,7 +432,6 @@
final SurfaceSession mFxSession;
Watermark mWatermark;
StrictModeFlash mStrictModeFlash;
- CircularDisplayMask mCircularDisplayMask;
FocusedStackFrame mFocusedStackFrame;
int mFocusedStackLayer;
@@ -559,9 +561,10 @@
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
- float mWindowAnimationScale = 1.0f;
- float mTransitionAnimationScale = 1.0f;
- float mAnimatorDurationScale = 1.0f;
+ float mWindowAnimationScaleSetting = 1.0f;
+ float mTransitionAnimationScaleSetting = 1.0f;
+ float mAnimatorDurationScaleSetting = 1.0f;
+ boolean mAnimationsDisabled = false;
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
@@ -777,6 +780,19 @@
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mPowerManagerInternal.setPolicy(mPolicy); // TODO: register as local service instead
+ mPowerManagerInternal.registerLowPowerModeObserver(
+ new PowerManagerInternal.LowPowerModeListener() {
+ @Override
+ public void onLowPowerModeChanged(boolean enabled) {
+ synchronized (mWindowMap) {
+ if (mAnimationsDisabled != enabled) {
+ mAnimationsDisabled = enabled;
+ dispatchNewAnimatorScaleLocked(null);
+ }
+ }
+ }
+ });
+ mAnimationsDisabled = mPowerManagerInternal.getLowPowerModeEnabled();
mScreenFrozenLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "SCREEN_FROZEN");
mScreenFrozenLock.setReferenceCounted(false);
@@ -796,12 +812,12 @@
);
// Get persisted window scale setting
- mWindowAnimationScale = Settings.Global.getFloat(context.getContentResolver(),
- Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
- mTransitionAnimationScale = Settings.Global.getFloat(context.getContentResolver(),
- Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+ mWindowAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
+ Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
+ mTransitionAnimationScaleSetting = Settings.Global.getFloat(context.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScaleSetting);
setAnimatorDurationScale(Settings.Global.getFloat(context.getContentResolver(),
- Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale));
+ Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting));
// Track changes to DevicePolicyManager state so we can enable/disable keyguard.
IntentFilter filter = new IntentFilter();
@@ -814,6 +830,7 @@
mAnimator = new WindowAnimator(this);
+ LocalServices.addService(WindowManagerInternal.class, new LocalService());
initPolicy();
// Add ourself to the Watchdog monitors.
@@ -827,9 +844,6 @@
} finally {
SurfaceControl.closeTransaction();
}
-
- LocalServices.addService(WindowManagerInternal.class, new LocalService());
- showCircularDisplayMaskIfNeeded();
}
public InputMonitor getInputMonitor() {
@@ -1469,10 +1483,15 @@
if (pos >= 0) {
final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken;
- if (pos < windows.size()) {
- WindowState wp = windows.get(pos);
- if (wp == mInputMethodWindow) {
- pos++;
+ // Skip windows owned by the input method.
+ if (mInputMethodWindow != null) {
+ while (pos < windows.size()) {
+ WindowState wp = windows.get(pos);
+ if (wp == mInputMethodWindow || wp.mAttachedWindow == mInputMethodWindow) {
+ pos++;
+ continue;
+ }
+ break;
}
}
if (DEBUG_INPUT_METHOD) Slog.v(TAG, "Adding " + N + " dialogs at pos=" + pos);
@@ -2792,14 +2811,13 @@
performLayoutAndPlaceSurfacesLocked();
}
- public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
+ public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
synchronized (mWindowMap) {
if (mAccessibilityController != null) {
WindowState window = mWindowMap.get(token);
//TODO (multidisplay): Magnification is supported only for the default display.
if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
- mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle,
- immediate);
+ mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle);
}
}
}
@@ -5185,9 +5203,9 @@
scale = fixScale(scale);
switch (which) {
- case 0: mWindowAnimationScale = scale; break;
- case 1: mTransitionAnimationScale = scale; break;
- case 2: mAnimatorDurationScale = scale; break;
+ case 0: mWindowAnimationScaleSetting = scale; break;
+ case 1: mTransitionAnimationScaleSetting = scale; break;
+ case 2: mAnimatorDurationScaleSetting = scale; break;
}
// Persist setting
@@ -5203,13 +5221,14 @@
if (scales != null) {
if (scales.length >= 1) {
- mWindowAnimationScale = fixScale(scales[0]);
+ mWindowAnimationScaleSetting = fixScale(scales[0]);
}
if (scales.length >= 2) {
- mTransitionAnimationScale = fixScale(scales[1]);
+ mTransitionAnimationScaleSetting = fixScale(scales[1]);
}
if (scales.length >= 3) {
- setAnimatorDurationScale(fixScale(scales[2]));
+ mAnimatorDurationScaleSetting = fixScale(scales[2]);
+ dispatchNewAnimatorScaleLocked(null);
}
}
@@ -5218,24 +5237,43 @@
}
private void setAnimatorDurationScale(float scale) {
- mAnimatorDurationScale = scale;
+ mAnimatorDurationScaleSetting = scale;
ValueAnimator.setDurationScale(scale);
}
+ public float getWindowAnimationScaleLocked() {
+ return mAnimationsDisabled ? 0 : mWindowAnimationScaleSetting;
+ }
+
+ public float getTransitionAnimationScaleLocked() {
+ return mAnimationsDisabled ? 0 : mTransitionAnimationScaleSetting;
+ }
+
@Override
public float getAnimationScale(int which) {
switch (which) {
- case 0: return mWindowAnimationScale;
- case 1: return mTransitionAnimationScale;
- case 2: return mAnimatorDurationScale;
+ case 0: return mWindowAnimationScaleSetting;
+ case 1: return mTransitionAnimationScaleSetting;
+ case 2: return mAnimatorDurationScaleSetting;
}
return 0;
}
@Override
public float[] getAnimationScales() {
- return new float[] { mWindowAnimationScale, mTransitionAnimationScale,
- mAnimatorDurationScale };
+ return new float[] { mWindowAnimationScaleSetting, mTransitionAnimationScaleSetting,
+ mAnimatorDurationScaleSetting };
+ }
+
+ @Override
+ public float getCurrentAnimatorScale() {
+ synchronized(mWindowMap) {
+ return mAnimationsDisabled ? 0 : mAnimatorDurationScaleSetting;
+ }
+ }
+
+ void dispatchNewAnimatorScaleLocked(Session session) {
+ mH.obtainMessage(H.NEW_ANIMATOR_SCALE, session).sendToTarget();
}
@Override
@@ -5536,39 +5574,6 @@
}
}
- public void showCircularDisplayMaskIfNeeded() {
- // we're fullscreen and not hosted in an ActivityView
- if (mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_windowIsRound)) {
- mH.sendMessage(mH.obtainMessage(H.SHOW_DISPLAY_MASK));
- }
- }
-
- public void showCircularMask() {
- synchronized(mWindowMap) {
-
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- ">>> OPEN TRANSACTION showDisplayMask");
- SurfaceControl.openTransaction();
- try {
- // TODO(multi-display): support multiple displays
- if (mCircularDisplayMask == null) {
- mCircularDisplayMask = new CircularDisplayMask(
- getDefaultDisplayContentLocked().getDisplay(),
- mFxSession,
- mPolicy.windowTypeToLayerLw(
- WindowManager.LayoutParams.TYPE_POINTER)
- * TYPE_LAYER_MULTIPLIER + 10);
- }
- mCircularDisplayMask.setVisibility(true);
- } finally {
- SurfaceControl.closeTransaction();
- if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
- "<<< CLOSE TRANSACTION showDisplayMask");
- }
- }
- }
-
// TODO: more accounting of which pid(s) turned it on, keep count,
// only allow disables from pids which have count on, etc.
@Override
@@ -6093,7 +6098,7 @@
&& screenRotationAnimation.hasScreenshot()) {
if (screenRotationAnimation.setRotationInTransaction(
rotation, mFxSession,
- MAX_ANIMATION_DURATION, mTransitionAnimationScale,
+ MAX_ANIMATION_DURATION, getTransitionAnimationScaleLocked(),
displayInfo.logicalWidth, displayInfo.logicalHeight)) {
scheduleAnimationLocked();
}
@@ -7175,7 +7180,9 @@
public static final int TAP_OUTSIDE_STACK = 31;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
- public static final int SHOW_DISPLAY_MASK = 33;
+ public static final int ALL_WINDOWS_DRAWN = 33;
+
+ public static final int NEW_ANIMATOR_SCALE = 34;
@Override
public void handleMessage(Message msg) {
@@ -7427,11 +7434,12 @@
case PERSIST_ANIMATION_SCALE: {
Settings.Global.putFloat(mContext.getContentResolver(),
- Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScale);
+ Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
Settings.Global.putFloat(mContext.getContentResolver(),
- Settings.Global.TRANSITION_ANIMATION_SCALE, mTransitionAnimationScale);
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ mTransitionAnimationScaleSetting);
Settings.Global.putFloat(mContext.getContentResolver(),
- Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScale);
+ Settings.Global.ANIMATOR_DURATION_SCALE, mAnimatorDurationScaleSetting);
break;
}
@@ -7552,17 +7560,18 @@
}
case WAITING_FOR_DRAWN_TIMEOUT: {
- Pair<WindowState, IRemoteCallback> pair;
+ IRemoteCallback callback = null;
synchronized (mWindowMap) {
- pair = (Pair<WindowState, IRemoteCallback>)msg.obj;
- Slog.w(TAG, "Timeout waiting for drawn: " + pair.first);
- if (!mWaitingForDrawn.remove(pair)) {
- return;
- }
+ Slog.w(TAG, "Timeout waiting for drawn: undrawn=" + mWaitingForDrawn);
+ mWaitingForDrawn.clear();
+ callback = mWaitingForDrawnCallback;
+ mWaitingForDrawnCallback = null;
}
- try {
- pair.second.sendResult(null);
- } catch (RemoteException e) {
+ if (callback != null) {
+ try {
+ callback.sendResult(null);
+ } catch (RemoteException e) {
+ }
}
break;
}
@@ -7572,11 +7581,6 @@
break;
}
- case SHOW_DISPLAY_MASK: {
- showCircularMask();
- break;
- }
-
case DO_ANIMATION_CALLBACK: {
try {
((IRemoteCallback)msg.obj).sendResult(null);
@@ -7620,6 +7624,46 @@
} catch (RemoteException e) {
}
break;
+ case ALL_WINDOWS_DRAWN: {
+ IRemoteCallback callback;
+ synchronized (mWindowMap) {
+ callback = mWaitingForDrawnCallback;
+ mWaitingForDrawnCallback = null;
+ }
+ if (callback != null) {
+ try {
+ callback.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ case NEW_ANIMATOR_SCALE: {
+ float scale = getCurrentAnimatorScale();
+ ValueAnimator.setDurationScale(scale);
+ Session session = (Session)msg.obj;
+ if (session != null) {
+ try {
+ session.mCallback.onAnimatorScaleChanged(scale);
+ } catch (RemoteException e) {
+ }
+ } else {
+ ArrayList<IWindowSessionCallback> callbacks
+ = new ArrayList<IWindowSessionCallback>();
+ synchronized (mWindowMap) {
+ for (int i=0; i<mSessions.size(); i++) {
+ callbacks.add(mSessions.valueAt(i).mCallback);
+ }
+
+ }
+ for (int i=0; i<callbacks.size(); i++) {
+ try {
+ callbacks.get(i).onAnimatorScaleChanged(scale);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
@@ -7632,11 +7676,11 @@
// -------------------------------------------------------------
@Override
- public IWindowSession openSession(IInputMethodClient client,
+ public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
- Session session = new Session(this, client, inputContext);
+ Session session = new Session(this, callback, client, inputContext);
return session;
}
@@ -8398,7 +8442,7 @@
}
win.mLayoutNeeded = false;
win.prelayout();
- mPolicy.layoutWindowLw(win, win.mAttrs, null);
+ mPolicy.layoutWindowLw(win, null);
win.mLayoutSeq = seq;
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame="
+ win.mFrame + " mContainingFrame="
@@ -8450,7 +8494,7 @@
}
win.mLayoutNeeded = false;
win.prelayout();
- mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);
+ mPolicy.layoutWindowLw(win, win.mAttachedWindow);
win.mLayoutSeq = seq;
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame="
+ win.mFrame + " mContainingFrame="
@@ -8746,7 +8790,7 @@
displayInfo.appWidth, displayInfo.appHeight, transit);
appAnimator.thumbnailAnimation = anim;
anim.restrictDuration(MAX_ANIMATION_DURATION);
- anim.scaleCurrentDuration(mTransitionAnimationScale);
+ anim.scaleCurrentDuration(getTransitionAnimationScaleLocked());
Point p = new Point();
mAppTransition.getStartingPoint(p);
appAnimator.thumbnailX = p.x;
@@ -9052,9 +9096,6 @@
if (mStrictModeFlash != null) {
mStrictModeFlash.positionSurface(defaultDw, defaultDh);
}
- if (mCircularDisplayMask != null) {
- mCircularDisplayMask.positionSurface(defaultDw, defaultDh, mRotation);
- }
boolean focusDisplayed = false;
@@ -9185,6 +9226,13 @@
winAnimator.setAnimation(a);
winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left;
winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top;
+
+ //TODO (multidisplay): Accessibility supported only for the default display.
+ if (mAccessibilityController != null
+ && displayId == Display.DEFAULT_DISPLAY) {
+ mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
+
try {
w.mClient.moved(w.mFrame.left, w.mFrame.top);
} catch (RemoteException e) {
@@ -9558,53 +9606,30 @@
}
void checkDrawnWindowsLocked() {
- if (mWaitingForDrawn.size() > 0) {
- for (int j=mWaitingForDrawn.size()-1; j>=0; j--) {
- Pair<WindowState, IRemoteCallback> pair = mWaitingForDrawn.get(j);
- WindowState win = pair.first;
- //Slog.i(TAG, "Waiting for drawn " + win + ": removed="
- // + win.mRemoved + " visible=" + win.isVisibleLw()
- // + " shown=" + win.mSurfaceShown);
- if (win.mRemoved) {
- // Window has been removed; no draw will now happen, so stop waiting.
- Slog.w(TAG, "Aborted waiting for drawn: " + pair.first);
- try {
- pair.second.sendResult(null);
- } catch (RemoteException e) {
- }
- mWaitingForDrawn.remove(pair);
- mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
- } else if (win.mWinAnimator.mSurfaceShown) {
- // Window is now drawn (and shown).
- try {
- pair.second.sendResult(null);
- } catch (RemoteException e) {
- }
- mWaitingForDrawn.remove(pair);
- mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
- }
+ if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
+ return;
+ }
+ for (int j = mWaitingForDrawn.size() - 1; j >= 0; j--) {
+ WindowState win = mWaitingForDrawn.get(j);
+ if (DEBUG_SCREEN_ON) Slog.i(TAG, "Waiting for drawn " + win +
+ ": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
+ " mHasSurface=" + win.mHasSurface +
+ " drawState=" + win.mWinAnimator.mDrawState);
+ if (win.mRemoved || !win.mHasSurface) {
+ // Window has been removed; no draw will now happen, so stop waiting.
+ if (DEBUG_SCREEN_ON) Slog.w(TAG, "Aborted waiting for drawn: " + win);
+ mWaitingForDrawn.remove(win);
+ } else if (win.hasDrawnLw()) {
+ // Window is now drawn (and shown).
+ if (DEBUG_SCREEN_ON) Slog.d(TAG, "Window drawn win=" + win);
+ mWaitingForDrawn.remove(win);
}
}
- }
-
- @Override
- public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
- if (token != null && callback != null) {
- synchronized (mWindowMap) {
- WindowState win = windowForClientLocked(null, token, true);
- if (win != null) {
- Pair<WindowState, IRemoteCallback> pair =
- new Pair<WindowState, IRemoteCallback>(win, callback);
- Message m = mH.obtainMessage(H.WAITING_FOR_DRAWN_TIMEOUT, pair);
- mH.sendMessageDelayed(m, 2000);
- mWaitingForDrawn.add(pair);
- checkDrawnWindowsLocked();
- return true;
- }
- Slog.i(TAG, "waitForWindowDrawn: win null");
- }
+ if (mWaitingForDrawn.isEmpty()) {
+ if (DEBUG_SCREEN_ON) Slog.d(TAG, "All windows drawn!");
+ mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
+ mH.sendEmptyMessage(H.ALL_WINDOWS_DRAWN);
}
- return false;
}
void setHoldScreenLocked(final Session newHoldScreen) {
@@ -9846,7 +9871,9 @@
mCurrentFocus = newFocus;
mLosingFocus.remove(newFocus);
- if (mAccessibilityController != null) {
+ // TODO(multidisplay): Accessibilty supported only of default desiplay.
+ if (mAccessibilityController != null
+ && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
mAccessibilityController.onWindowFocusChangedLocked();
}
@@ -10077,7 +10104,7 @@
mExitAnimId = mEnterAnimId = 0;
}
if (screenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
- mTransitionAnimationScale, displayInfo.logicalWidth,
+ getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
scheduleAnimationLocked();
} else {
@@ -10412,13 +10439,10 @@
void dumpSessionsLocked(PrintWriter pw, boolean dumpAll) {
pw.println("WINDOW MANAGER SESSIONS (dumpsys window sessions)");
- if (mSessions.size() > 0) {
- Iterator<Session> it = mSessions.iterator();
- while (it.hasNext()) {
- Session s = it.next();
- pw.print(" Session "); pw.print(s); pw.println(':');
- s.dump(pw, " ");
- }
+ for (int i=0; i<mSessions.size(); i++) {
+ Session s = mSessions.valueAt(i);
+ pw.print(" Session "); pw.print(s); pw.println(':');
+ s.dump(pw, " ");
}
}
@@ -10552,9 +10576,8 @@
pw.println();
pw.println(" Clients waiting for these windows to be drawn:");
for (int i=mWaitingForDrawn.size()-1; i>=0; i--) {
- Pair<WindowState, IRemoteCallback> pair = mWaitingForDrawn.get(i);
- pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(pair.first);
- pw.print(": "); pw.println(pair.second);
+ WindowState win = mWaitingForDrawn.get(i);
+ pw.print(" Waiting #"); pw.print(i); pw.print(' '); pw.print(win);
}
}
pw.println();
@@ -10623,9 +10646,10 @@
pw.print(" mLastWindowForcedOrientation="); pw.print(mLastWindowForcedOrientation);
pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
pw.print(" mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
- pw.print(" mWindowAnimationScale="); pw.print(mWindowAnimationScale);
- pw.print(" mTransitionWindowAnimationScale="); pw.print(mTransitionAnimationScale);
- pw.print(" mAnimatorDurationScale="); pw.println(mAnimatorDurationScale);
+ pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
+ pw.print(" window="); pw.print(mWindowAnimationScaleSetting);
+ pw.print(" transition="); pw.print(mTransitionAnimationScaleSetting);
+ pw.print(" animator="); pw.println(mAnimatorDurationScaleSetting);
pw.print(" mTraversalScheduled="); pw.println(mTraversalScheduled);
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);
pw.print(" mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
@@ -11064,7 +11088,7 @@
}
mAccessibilityController.setMagnificationCallbacksLocked(callbacks);
if (!mAccessibilityController.hasCallbacksLocked()) {
- mAccessibilityController = null;
+ mAccessibilityController = null;
}
}
}
@@ -11078,7 +11102,7 @@
}
mAccessibilityController.setWindowsForAccessibilityCallback(callback);
if (!mAccessibilityController.hasCallbacksLocked()) {
- mAccessibilityController = null;
+ mAccessibilityController = null;
}
}
}
@@ -11115,5 +11139,25 @@
}
}
}
+
+ public void waitForAllWindowsDrawn(IRemoteCallback callback, long timeout) {
+ synchronized (mWindowMap) {
+ mWaitingForDrawnCallback = callback;
+ final WindowList windows = getDefaultWindowListLocked();
+ for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+ final WindowState win = windows.get(winNdx);
+ if (win.mHasSurface) {
+ win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ // Force add to mResizingWindows.
+ win.mLastContentInsets.set(-1, -1, -1, -1);
+ mWaitingForDrawn.add(win);
+ }
+ }
+ requestTraversalLocked();
+ mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
+ mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);
+ }
+ checkDrawnWindowsLocked();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4a80e3e..7003c8c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -36,6 +36,7 @@
import android.os.RemoteCallbackList;
import android.os.SystemClock;
import android.util.TimeUtils;
+import android.view.Display;
import android.view.IWindowFocusObserver;
import android.view.IWindowId;
import com.android.server.input.InputWindowHandle;
@@ -727,8 +728,11 @@
}
public DisplayContent getDisplayContent() {
- return mAppToken == null || mNotOnAppsDisplay ?
- mDisplayContent : getStack().getDisplayContent();
+ if (mAppToken == null || mNotOnAppsDisplay) {
+ return mDisplayContent;
+ }
+ final TaskStack stack = getStack();
+ return stack == null ? mDisplayContent : stack.getDisplayContent();
}
public int getDisplayId() {
@@ -1359,6 +1363,13 @@
mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, reportDraw,
newConfig);
}
+
+ //TODO (multidisplay): Accessibility supported only for the default display.
+ if (mService.mAccessibilityController != null
+ && getDisplayId() == Display.DEFAULT_DISPLAY) {
+ mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+ }
+
mOverscanInsetsChanged = false;
mContentInsetsChanged = false;
mVisibleInsetsChanged = false;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e257ebc..bda10de 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -207,7 +207,7 @@
mLocalAnimating = false;
mAnimation = anim;
mAnimation.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
- mAnimation.scaleCurrentDuration(mService.mWindowAnimationScale);
+ mAnimation.scaleCurrentDuration(mService.getWindowAnimationScaleLocked());
// Start out animation gone if window is gone, or visible if window is visible.
mTransformation.clear();
mTransformation.setAlpha(mLastHidden ? 0 : 1);
@@ -283,7 +283,7 @@
" @ " + currentTime + ": ww=" + mWin.mFrame.width() +
" wh=" + mWin.mFrame.height() +
" dw=" + mAnimDw + " dh=" + mAnimDh +
- " scale=" + mService.mWindowAnimationScale);
+ " scale=" + mService.getWindowAnimationScaleLocked());
mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
mAnimDw, mAnimDh);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 3cfb45b..db44d3a 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -12,7 +12,6 @@
$(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
$(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \
$(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \
- $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecService.cpp \
$(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiMhlController.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
$(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
index a734026..0f919d1 100644
--- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -57,12 +57,21 @@
int getVersion();
// Get vendor id used for vendor command.
uint32_t getVendorId();
+ // Get Port information on all the HDMI ports.
+ jobjectArray getPortInfos();
+ // Set a flag and its value.
+ void setOption(int flag, int value);
+ // Set audio return channel status.
+ void setAudioReturnChannel(bool flag);
+ // Whether to hdmi device is connected to the given port.
+ bool isConnected(int port);
jobject getCallbacksObj() const {
return mCallbacksObj;
}
private:
+ static const int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
static void onReceived(const hdmi_event_t* event, void* arg);
hdmi_cec_device_t* mDevice;
@@ -203,11 +212,11 @@
}
int HdmiCecController::getPhysicalAddress() {
- uint16_t physicalAddress = 0xFFFF;
- if (mDevice->get_physical_address(mDevice, &physicalAddress) == 0) {
- return physicalAddress;
+ uint16_t addr;
+ if (!mDevice->get_physical_address(mDevice, &addr)) {
+ return addr;
}
- return -1;
+ return INVALID_PHYSICAL_ADDRESS;
}
int HdmiCecController::getVersion() {
@@ -222,6 +231,47 @@
return vendorId;
}
+jobjectArray HdmiCecController::getPortInfos() {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jclass hdmiPortInfo = env->FindClass("android/hardware/hdmi/HdmiPortInfo");
+ if (hdmiPortInfo == NULL) {
+ return NULL;
+ }
+ jmethodID ctor = env->GetMethodID(hdmiPortInfo, "<init>", "(IIIZZZ)V");
+ if (ctor == NULL) {
+ return NULL;
+ }
+ hdmi_port_info* ports;
+ int numPorts;
+ mDevice->get_port_info(mDevice, &ports, &numPorts);
+ jobjectArray res = env->NewObjectArray(numPorts, hdmiPortInfo, NULL);
+
+ // MHL support field will be obtained from MHL HAL. Leave it to false.
+ jboolean mhlSupported = (jboolean) 0;
+ for (int i = 0; i < numPorts; ++i) {
+ hdmi_port_info* info = &ports[i];
+ jboolean cecSupported = (jboolean) info->cec_supported;
+ jboolean arcSupported = (jboolean) info->arc_supported;
+ jobject infoObj = env->NewObject(hdmiPortInfo, ctor, info->port_num, info->type,
+ info->physical_address, cecSupported, mhlSupported, arcSupported);
+ env->SetObjectArrayElement(res, i, infoObj);
+ }
+ return res;
+}
+
+void HdmiCecController::setOption(int flag, int value) {
+ mDevice->set_option(mDevice, flag, value);
+}
+
+// Set audio return channel status.
+void HdmiCecController::setAudioReturnChannel(bool enabled) {
+ mDevice->set_audio_return_channel(mDevice, enabled ? 1 : 0);
+}
+
+// Whether to hdmi device is connected to the given port.
+bool HdmiCecController::isConnected(int port) {
+ return mDevice->is_connected(mDevice, port) == HDMI_CONNECTED;
+}
// static
void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
@@ -291,41 +341,53 @@
return controller->sendMessage(message);
}
-static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz,
- jlong controllerPtr, jint logicalAddress) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
- return controller->addLogicalAddress(
- static_cast<cec_logical_address_t>(logicalAddress));
+static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr,
+ jint logicalAddress) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
+ return controller->addLogicalAddress(static_cast<cec_logical_address_t>(logicalAddress));
}
-static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz,
- jlong controllerPtr) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
controller->clearLogicaladdress();
}
-static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz,
- jlong controllerPtr) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, jlong controllerPtr) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
return controller->getPhysicalAddress();
}
-static jint nativeGetVersion(JNIEnv* env, jclass clazz,
- jlong controllerPtr) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+static jint nativeGetVersion(JNIEnv* env, jclass clazz, jlong controllerPtr) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
return controller->getVersion();
}
static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) {
- HdmiCecController* controller =
- reinterpret_cast<HdmiCecController*>(controllerPtr);
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
return controller->getVendorId();
}
+static jobjectArray nativeGetPortInfos(JNIEnv* env, jclass clazz, jlong controllerPtr) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
+ return controller->getPortInfos();
+}
+
+static void nativeSetOption(JNIEnv* env, jclass clazz, jlong controllerPtr, jint flag, jint value) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
+ controller->setOption(flag, value);
+}
+
+static void nativeSetAudioReturnChannel(JNIEnv* env, jclass clazz, jlong controllerPtr,
+ jboolean enabled) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
+ controller->setAudioReturnChannel(enabled == JNI_TRUE);
+}
+
+static jboolean nativeIsConnected(JNIEnv* env, jclass clazz, jlong controllerPtr, jint port) {
+ HdmiCecController* controller = reinterpret_cast<HdmiCecController*>(controllerPtr);
+ return controller->isConnected(port) ? JNI_TRUE : JNI_FALSE ;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit",
@@ -337,13 +399,18 @@
{ "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress },
{ "nativeGetVersion", "(J)I", (void *) nativeGetVersion },
{ "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId },
+ { "nativeGetPortInfos",
+ "(J)[Landroid/hardware/hdmi/HdmiPortInfo;",
+ (void *) nativeGetPortInfos },
+ { "nativeSetOption", "(JII)V", (void *) nativeSetOption },
+ { "nativeSetAudioReturnChannel", "(JZ)V", (void *) nativeSetAudioReturnChannel },
+ { "nativeIsConnected", "(JI)Z", (void *) nativeIsConnected },
};
#define CLASS_PATH "com/android/server/hdmi/HdmiCecController"
int register_android_server_hdmi_HdmiCecController(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods,
- NELEM(sMethods));
+ int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
return 0;
}
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
deleted file mode 100644
index 1d111a1..0000000
--- a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
+++ /dev/null
@@ -1,756 +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.
- */
-
-#define LOG_TAG "HdmiCecJni"
-
-#define LOG_NDEBUG 1
-
-#include "ScopedPrimitiveArray.h"
-
-#include <string>
-#include <deque>
-#include <map>
-
-#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/Log.h>
-#include <hardware/hdmi_cec.h>
-
-namespace android {
-
-static struct {
- jmethodID handleMessage;
- jmethodID handleHotplug;
- jmethodID getActiveSource;
- jmethodID getLanguage;
-} gHdmiCecServiceClassInfo;
-
-#ifndef min
-#define min(a, b) ((a) > (b) ? (b) : (a))
-#endif
-
-class HdmiCecHandler {
-public:
- enum HdmiCecError {
- SUCCESS = 0,
- FAILED = -1
- };
-
- // Data type to hold a CEC message or internal event data.
- typedef union {
- cec_message_t cec;
- hotplug_event_t hotplug;
- } queue_item_t;
-
- // Entry used for message queue.
- typedef std::pair<int, const queue_item_t> MessageEntry;
-
- HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj);
-
- void initialize();
-
- // initialize individual logical device.
- cec_logical_address_t initLogicalDevice(cec_device_type_t type);
- void releaseLogicalDevice(cec_device_type_t type);
-
- cec_logical_address_t getLogicalAddress(cec_device_type_t deviceType);
- uint16_t getPhysicalAddress();
- cec_device_type_t getDeviceType(cec_logical_address_t addr);
- void queueMessage(const MessageEntry& message);
- void queueOutgoingMessage(const cec_message_t& message);
- void sendReportPhysicalAddress(cec_logical_address_t srcAddr);
- void sendActiveSource(cec_logical_address_t srcAddr);
- void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
- int opcode, int reason);
- void sendCecVersion(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
- int version);
- void sendDeviceVendorId(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
- void sendGiveDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
- void sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
- const char* name, size_t len);
- void sendSetMenuLanguage(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
-
- void sendCecMessage(const cec_message_t& message);
- void setOsdName(const char* name, size_t len);
-
-private:
- enum {
- EVENT_TYPE_RX,
- EVENT_TYPE_TX,
- EVENT_TYPE_HOTPLUG,
- EVENT_TYPE_STANDBY
- };
-
- /*
- * logical address pool for each device type.
- */
- static const cec_logical_address_t TV_ADDR_POOL[];
- static const cec_logical_address_t PLAYBACK_ADDR_POOL[];
- static const cec_logical_address_t RECORDER_ADDR_POOL[];
- static const cec_logical_address_t TUNER_ADDR_POOL[];
-
- static const unsigned int MAX_BUFFER_SIZE = 256;
- static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF;
-
- static void onReceived(const hdmi_event_t* event, void* arg);
- static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
-
- void updatePhysicalAddress();
- void updateLogicalAddress();
-
- // Allocate logical address. The CEC standard recommends that we try to use the address
- // we have ever used before, in case this is to allocate an address afte the cable is
- // connected again. If preferredAddr is given a valid one (not CEC_ADDR_UNREGISTERED), then
- // this method checks if the address is available first. If not, it tries other addresses
- // int the address pool available for the given type.
- cec_logical_address_t allocateLogicalAddress(cec_device_type_t type,
- cec_logical_address_t preferredAddr);
-
- // Send a CEC ping message. Returns true if successful.
- bool sendPing(cec_logical_address_t addr);
-
- // Return the pool of logical addresses that are used for a given device type.
- // One of the addresses in the pool will be chosen in the allocation logic.
- bool getLogicalAddressPool(cec_device_type_t type, const cec_logical_address_t** addrPool,
- size_t* poolSize);
-
- // Handles the message retrieved from internal message queue. The message can be
- // for either rx or tx.
- void dispatchMessage(const MessageEntry& message);
- void processIncomingMessage(const cec_message_t& msg);
-
- // Check the message before we pass it up to framework. If true, we proceed.
- // otherwise do not propagate it.
- bool precheckMessage(const cec_message_t& msg);
-
- // Propagate the message up to Java layer.
- void propagateMessage(const cec_message_t& msg);
- void propagateHotplug(bool connected);
-
- // Handles incoming <Request Active Source> message. If one of logical
- // devices is active, it should reply with <Active Source> message.
- void handleRequestActiveSource();
- void handleGiveOsdName(const cec_message_t& msg);
- void handleGiveDeviceVendorID(const cec_message_t& msg);
- void handleGetCECVersion(const cec_message_t& msg);
- void handleGetMenuLanguage(const cec_message_t& msg);
-
- // Internal thread for message queue handler
- class HdmiThread : public Thread {
- public:
- HdmiThread(HdmiCecHandler* hdmiCecHandler, bool canCallJava) :
- Thread(canCallJava),
- mHdmiCecHandler(hdmiCecHandler) {
- }
- private:
- virtual bool threadLoop() {
- ALOGV("HdmiThread started");
- AutoMutex _l(mHdmiCecHandler->mMessageQueueLock);
- mHdmiCecHandler->mMessageQueueCondition.wait(mHdmiCecHandler->mMessageQueueLock);
- /* Process all messages in the queue */
- while (mHdmiCecHandler->mMessageQueue.size() > 0) {
- MessageEntry entry = mHdmiCecHandler->mMessageQueue.front();
- mHdmiCecHandler->dispatchMessage(entry);
- }
- return true;
- }
-
- HdmiCecHandler* mHdmiCecHandler;
- };
-
- // device type -> logical address mapping
- std::map<cec_device_type_t, cec_logical_address_t> mLogicalDevices;
-
- hdmi_cec_device_t* mDevice;
- jobject mCallbacksObj;
- Mutex mLock;
- Mutex mMessageQueueLock;
- Condition mMessageQueueCondition;
- sp<HdmiThread> mMessageQueueHandler;
-
- std::deque<MessageEntry> mMessageQueue;
- uint16_t mPhysicalAddress;
- std::string mOsdName;
-};
-
- const cec_logical_address_t HdmiCecHandler::TV_ADDR_POOL[] = {
- CEC_ADDR_TV,
- CEC_ADDR_FREE_USE,
- };
-
- const cec_logical_address_t HdmiCecHandler::PLAYBACK_ADDR_POOL[] = {
- CEC_ADDR_PLAYBACK_1,
- CEC_ADDR_PLAYBACK_2,
- CEC_ADDR_PLAYBACK_3
- };
-
- const cec_logical_address_t HdmiCecHandler::RECORDER_ADDR_POOL[] = {
- CEC_ADDR_RECORDER_1,
- CEC_ADDR_RECORDER_2,
- CEC_ADDR_RECORDER_3
- };
-
- const cec_logical_address_t HdmiCecHandler::TUNER_ADDR_POOL[] = {
- CEC_ADDR_TUNER_1,
- CEC_ADDR_TUNER_2,
- CEC_ADDR_TUNER_3,
- CEC_ADDR_TUNER_4
- };
-
-HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) :
- mDevice(device),
- mCallbacksObj(callbacksObj) {
-}
-
-void HdmiCecHandler::initialize() {
- mDevice->register_event_callback(mDevice, HdmiCecHandler::onReceived, this);
- mMessageQueueHandler = new HdmiThread(this, true /* canCallJava */);
- mMessageQueueHandler->run("MessageHandler");
- updatePhysicalAddress();
-}
-
-uint16_t HdmiCecHandler::getPhysicalAddress() {
- return mPhysicalAddress;
-}
-
-cec_logical_address_t HdmiCecHandler::initLogicalDevice(cec_device_type_t type) {
- cec_logical_address addr = allocateLogicalAddress(type, CEC_ADDR_UNREGISTERED);
- if (addr != CEC_ADDR_UNREGISTERED && !mDevice->add_logical_address(mDevice, addr)) {
- mLogicalDevices.insert(std::pair<cec_device_type_t, cec_logical_address_t>(type, addr));
-
- // Broadcast <Report Physical Address> when a new logical address was allocated to let
- // other devices discover the new logical device and its logical - physical address
- // association.
- sendReportPhysicalAddress(addr);
- }
- return addr;
-}
-
-void HdmiCecHandler::releaseLogicalDevice(cec_device_type_t type) {
- std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.find(type);
- if (it != mLogicalDevices.end()) {
- mLogicalDevices.erase(it);
- }
- // TODO: remove the address monitored in HAL as well.
-}
-
-cec_logical_address_t HdmiCecHandler::getLogicalAddress(cec_device_type_t type) {
- std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.find(type);
- if (it != mLogicalDevices.end()) {
- return it->second;
- }
- return CEC_ADDR_UNREGISTERED;
-}
-
-cec_device_type_t HdmiCecHandler::getDeviceType(cec_logical_address_t addr) {
- std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
- for (; it != mLogicalDevices.end(); ++it) {
- if (it->second == addr) {
- return it->first;
- }
- }
- return CEC_DEVICE_INACTIVE;
-}
-
-void HdmiCecHandler::queueMessage(const MessageEntry& entry) {
- AutoMutex _l(mMessageQueueLock);
- if (mMessageQueue.size() <= MAX_BUFFER_SIZE) {
- mMessageQueue.push_back(entry);
- mMessageQueueCondition.signal();
- } else {
- ALOGW("Queue is full! Message dropped.");
- }
-}
-
-void HdmiCecHandler::queueOutgoingMessage(const cec_message_t& message) {
- queue_item_t item;
- item.cec = message;
- MessageEntry entry = std::make_pair(EVENT_TYPE_TX, item);
- queueMessage(entry);
-}
-
-void HdmiCecHandler::sendReportPhysicalAddress(cec_logical_address_t addr) {
- if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
- ALOGE("Invalid physical address.");
- return;
- }
- cec_device_type_t deviceType = getDeviceType(addr);
- if (deviceType == CEC_DEVICE_INACTIVE) {
- ALOGE("Invalid logical address: %d", addr);
- return;
- }
-
- cec_message_t msg;
- msg.initiator = addr;
- msg.destination = CEC_ADDR_BROADCAST;
- msg.length = 4;
- msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS;
- msg.body[1] = (mPhysicalAddress >> 8) & 0xff;
- msg.body[2] = mPhysicalAddress & 0xff;
- msg.body[3] = deviceType;
- queueOutgoingMessage(msg);
-}
-
-void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) {
- if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
- ALOGE("Error getting physical address.");
- return;
- }
- cec_message_t msg;
- msg.initiator = srcAddr;
- msg.destination = CEC_ADDR_BROADCAST;
- msg.length = 3;
- msg.body[0] = CEC_MESSAGE_ACTIVE_SOURCE;
- msg.body[1] = (mPhysicalAddress >> 8) & 0xff;
- msg.body[2] = mPhysicalAddress & 0xff;
- queueOutgoingMessage(msg);
-}
-
-void HdmiCecHandler::sendFeatureAbort(cec_logical_address_t srcAddr,
- cec_logical_address_t dstAddr, int opcode, int reason) {
- cec_message_t msg;
- msg.initiator = srcAddr;
- msg.destination = dstAddr;
- msg.length = 3;
- msg.body[0] = CEC_MESSAGE_FEATURE_ABORT;
- msg.body[1] = opcode;
- msg.body[2] = reason;
- queueOutgoingMessage(msg);
-}
-
-void HdmiCecHandler::sendCecVersion(cec_logical_address_t srcAddr,
- cec_logical_address_t dstAddr, int version) {
- cec_message_t msg;
- msg.initiator = srcAddr;
- msg.destination = dstAddr;
- msg.length = 2;
- msg.body[0] = CEC_MESSAGE_CEC_VERSION;
- msg.body[1] = version;
- queueOutgoingMessage(msg);
-}
-
-void HdmiCecHandler::sendGiveDeviceVendorID(cec_logical_address_t srcAddr,
- cec_logical_address_t dstAddr) {
- cec_message_t msg;
- msg.initiator = srcAddr;
- msg.destination = dstAddr;
- msg.length = 1;
- msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID;
- queueOutgoingMessage(msg);
-}
-
-void HdmiCecHandler::sendDeviceVendorId(cec_logical_address_t srcAddr,
- cec_logical_address_t dstAddr) {
- cec_message_t msg;
- msg.initiator = srcAddr;
- msg.destination = dstAddr;
- msg.length = 4;
- msg.body[0] = CEC_MESSAGE_DEVICE_VENDOR_ID;
- uint32_t vendor_id;
- mDevice->get_vendor_id(mDevice, &vendor_id);
- msg.body[1] = (vendor_id >> 16) & 0xff;
- msg.body[2] = (vendor_id >> 8) & 0xff;
- msg.body[3] = vendor_id & 0xff;
- queueOutgoingMessage(msg);
-}
-
-void HdmiCecHandler::sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
- const char* name, size_t len) {
- cec_message_t msg;
- msg.initiator = srcAddr;
- msg.destination = dstAddr;
- msg.body[0] = CEC_MESSAGE_SET_OSD_NAME;
- msg.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH);
- std::memcpy(msg.body + 1, name, msg.length - 1);
- queueOutgoingMessage(msg);
-}
-
-void HdmiCecHandler::sendSetMenuLanguage(cec_logical_address_t srcAddr,
- cec_logical_address_t dstAddr) {
- char lang[4]; // buffer for 3-letter language code
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jstring res = (jstring) env->CallObjectMethod(mCallbacksObj,
- gHdmiCecServiceClassInfo.getLanguage,
- getDeviceType(srcAddr));
- const char *clang = env->GetStringUTFChars(res, NULL);
- strlcpy(lang, clang, sizeof(lang));
- env->ReleaseStringUTFChars(res, clang);
-
- cec_message_t msg;
- msg.initiator = srcAddr;
- msg.destination = dstAddr;
- msg.length = 4; // opcode (1) + language code (3)
- msg.body[0] = CEC_MESSAGE_SET_MENU_LANGUAGE;
- std::memcpy(msg.body + 1, lang, 3);
- queueOutgoingMessage(msg);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-void HdmiCecHandler::sendCecMessage(const cec_message_t& message) {
- AutoMutex _l(mLock);
- ALOGV("sendCecMessage");
- mDevice->send_message(mDevice, &message);
-}
-
-void HdmiCecHandler::setOsdName(const char* name, size_t len) {
- mOsdName.assign(name, min(len, CEC_MESSAGE_BODY_MAX_LENGTH - 1));
-}
-
-// static
-void HdmiCecHandler::onReceived(const hdmi_event_t* event, void* arg) {
- HdmiCecHandler* handler = static_cast<HdmiCecHandler*>(arg);
- if (handler == NULL) {
- return;
- }
- queue_item_t item;
- if (event->type == HDMI_EVENT_CEC_MESSAGE) {
- item.cec = event->cec;
- MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_RX, item);
- handler->queueMessage(entry);
- } else if (event->type == HDMI_EVENT_HOT_PLUG) {
- item.hotplug = event->hotplug;
- MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_HOTPLUG, item);
- handler->queueMessage(entry);
- }
-}
-
-// static
-void HdmiCecHandler::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
- if (env->ExceptionCheck()) {
- ALOGE("An exception was thrown by callback '%s'.", methodName);
- LOGE_EX(env);
- env->ExceptionClear();
- }
-}
-
-void HdmiCecHandler::updatePhysicalAddress() {
- uint16_t addr;
- if (!mDevice->get_physical_address(mDevice, &addr)) {
- mPhysicalAddress = addr;
- } else {
- mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
- }
-}
-
-void HdmiCecHandler::updateLogicalAddress() {
- mDevice->clear_logical_address(mDevice);
- std::map<cec_device_type_t, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
- for (; it != mLogicalDevices.end(); ++it) {
- cec_logical_address_t addr;
- cec_logical_address_t preferredAddr = it->second;
- cec_device_type_t deviceType = it->first;
- addr = allocateLogicalAddress(deviceType, preferredAddr);
- if (!mDevice->add_logical_address(mDevice, addr)) {
- it->second = addr;
- } else {
- it->second = CEC_ADDR_UNREGISTERED;
- }
- }
-}
-
-cec_logical_address_t HdmiCecHandler::allocateLogicalAddress(cec_device_type_t type,
- cec_logical_address_t preferredAddr) {
- const cec_logical_address_t* addrPool;
- size_t poolSize;
- if (getLogicalAddressPool(type, &addrPool, &poolSize) < 0) {
- return CEC_ADDR_UNREGISTERED;
- }
- unsigned start = 0;
-
- // Find the index of preferred address in the pool. If not found, the start
- // position will be 0. This happens when the passed preferredAddr is set to
- // CEC_ADDR_UNREGISTERED, meaning that no preferred address is given.
- for (unsigned i = 0; i < poolSize; i++) {
- if (addrPool[i] == preferredAddr) {
- start = i;
- break;
- }
- }
- for (unsigned i = 0; i < poolSize; i++) {
- cec_logical_address_t addr = addrPool[(start + i) % poolSize];
- if (!sendPing(addr)) {
- // Failure in pinging means the address is available, not taken by any device.
- ALOGV("Logical Address Allocation success: %d", addr);
- return addr;
- }
- }
- ALOGE("Logical Address Allocation failed");
- return CEC_ADDR_UNREGISTERED;
-}
-
-bool HdmiCecHandler::sendPing(cec_logical_address addr) {
- cec_message_t msg;
- msg.initiator = msg.destination = addr;
- msg.length = 0;
- return !mDevice->send_message(mDevice, &msg);
-
-}
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
-bool HdmiCecHandler::getLogicalAddressPool(cec_device_type_t deviceType,
- const cec_logical_address_t** addrPool, size_t* poolSize) {
- switch (deviceType) {
- case CEC_DEVICE_TV:
- *addrPool = TV_ADDR_POOL;
- *poolSize = ARRAY_SIZE(TV_ADDR_POOL);
- break;
- case CEC_DEVICE_RECORDER:
- *addrPool = RECORDER_ADDR_POOL;
- *poolSize = ARRAY_SIZE(RECORDER_ADDR_POOL);
- break;
- case CEC_DEVICE_TUNER:
- *addrPool = TUNER_ADDR_POOL;
- *poolSize = ARRAY_SIZE(TUNER_ADDR_POOL);
- break;
- case CEC_DEVICE_PLAYBACK:
- *addrPool = PLAYBACK_ADDR_POOL;
- *poolSize = ARRAY_SIZE(PLAYBACK_ADDR_POOL);
- break;
- default:
- ALOGE("Unsupported device type: %d", deviceType);
- return false;
- }
- return true;
-}
-
-#undef ARRAY_SIZE
-
-void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) {
- int type = entry.first;
- mMessageQueueLock.unlock();
- if (type == EVENT_TYPE_RX) {
- mMessageQueue.pop_front();
- processIncomingMessage(entry.second.cec);
- } else if (type == EVENT_TYPE_TX) {
- sendCecMessage(entry.second.cec);
- mMessageQueue.pop_front();
- } else if (type == EVENT_TYPE_HOTPLUG) {
- mMessageQueue.pop_front();
- bool connected = entry.second.hotplug.connected;
- if (connected) {
- updatePhysicalAddress();
- updateLogicalAddress();
- }
- propagateHotplug(connected);
- }
- mMessageQueueLock.lock();
-}
-
-void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) {
- int opcode = msg.body[0];
- if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) {
- sendReportPhysicalAddress(msg.destination);
- } else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) {
- handleRequestActiveSource();
- } else if (opcode == CEC_MESSAGE_GIVE_OSD_NAME) {
- handleGiveOsdName(msg);
- } else if (opcode == CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID) {
- handleGiveDeviceVendorID(msg);
- } else if (opcode == CEC_MESSAGE_GET_CEC_VERSION) {
- handleGetCECVersion(msg);
- } else if (opcode == CEC_MESSAGE_GET_MENU_LANGUAGE) {
- handleGetMenuLanguage(msg);
- } else if (opcode == CEC_MESSAGE_ABORT) {
- // Compliance testing requires that abort message be responded with feature abort.
- sendFeatureAbort(msg.destination, msg.initiator, msg.body[0], ABORT_REFUSED);
- } else {
- if (precheckMessage(msg)) {
- propagateMessage(msg);
- }
- }
-}
-
-bool HdmiCecHandler::precheckMessage(const cec_message_t& msg) {
- // Check if this is the broadcast message coming to itself, which need not be passed
- // back to framework. This happens because CEC spec specifies that a physical device
- // may host multiple logical devices. A broadcast message sent by one of them therefore
- // should be able to reach the others by the loopback mechanism.
- //
- // Currently we don't deal with multiple logical devices, so this is not necessary.
- // It should be revisited once we support hosting multiple logical devices.
- int opcode = msg.body[0];
- if (msg.destination == CEC_ADDR_BROADCAST &&
- (opcode == CEC_MESSAGE_ACTIVE_SOURCE ||
- opcode == CEC_MESSAGE_SET_STREAM_PATH ||
- opcode == CEC_MESSAGE_INACTIVE_SOURCE)) {
- uint16_t senderAddr = (msg.body[1] << 8) + msg.body[2];
- if (senderAddr == mPhysicalAddress) {
- return false;
- }
- }
- return true;
-}
-
-void HdmiCecHandler::propagateMessage(const cec_message_t& msg) {
- int paramLen = msg.length - 1;
- jint srcAddr = msg.initiator;
- jint dstAddr = msg.destination;
- jint opcode = msg.body[0];
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jbyteArray params = env->NewByteArray(paramLen);
- const jbyte* body = reinterpret_cast<const jbyte *>(msg.body + 1);
- if (paramLen > 0) {
- env->SetByteArrayRegion(params, 0, paramLen, body);
- }
- env->CallVoidMethod(mCallbacksObj,
- gHdmiCecServiceClassInfo.handleMessage,
- srcAddr, dstAddr, opcode, params);
- env->DeleteLocalRef(params);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-void HdmiCecHandler::propagateHotplug(bool connected) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- env->CallVoidMethod(mCallbacksObj,
- gHdmiCecServiceClassInfo.handleHotplug,
- connected);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-
-void HdmiCecHandler::handleRequestActiveSource() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jint activeDeviceType = env->CallIntMethod(mCallbacksObj,
- gHdmiCecServiceClassInfo.getActiveSource);
- if (activeDeviceType != CEC_DEVICE_INACTIVE) {
- sendActiveSource(getLogicalAddress(static_cast<cec_device_type_t>(activeDeviceType)));
- }
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-void HdmiCecHandler::handleGiveOsdName(const cec_message_t& msg) {
- if (!mOsdName.empty()) {
- sendSetOsdName(msg.destination, msg.initiator, mOsdName.c_str(), mOsdName.length());
- }
-}
-
-void HdmiCecHandler::handleGiveDeviceVendorID(const cec_message_t& msg) {
- sendDeviceVendorId(msg.destination, msg.initiator);
-}
-
-void HdmiCecHandler::handleGetCECVersion(const cec_message_t& msg) {
- int version;
- mDevice->get_version(mDevice, &version);
- sendCecVersion(msg.destination, msg.initiator, version);
-}
-
-void HdmiCecHandler::handleGetMenuLanguage(const cec_message_t& msg) {
- sendSetMenuLanguage(msg.destination, msg.initiator);
-}
-
-//------------------------------------------------------------------------------
-
-#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
- var = env->GetMethodID(clazz, methodName, methodDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find method " methodName);
-
-static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
- int err;
- hw_module_t* module;
- err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, const_cast<const hw_module_t **>(&module));
- if (err != 0) {
- ALOGE("Error acquiring hardware module: %d", err);
- return 0;
- }
- hw_device_t* device;
- err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device);
- if (err != 0) {
- ALOGE("Error opening hardware module: %d", err);
- return 0;
- }
- HdmiCecHandler *handler = new HdmiCecHandler(reinterpret_cast<hdmi_cec_device *>(device),
- env->NewGlobalRef(callbacksObj));
- handler->initialize();
-
- GET_METHOD_ID(gHdmiCecServiceClassInfo.handleMessage, clazz,
- "handleMessage", "(III[B)V");
- GET_METHOD_ID(gHdmiCecServiceClassInfo.handleHotplug, clazz,
- "handleHotplug", "(Z)V");
- GET_METHOD_ID(gHdmiCecServiceClassInfo.getActiveSource, clazz,
- "getActiveSource", "()I");
- GET_METHOD_ID(gHdmiCecServiceClassInfo.getLanguage, clazz,
- "getLanguage", "(I)Ljava/lang/String;");
-
- return reinterpret_cast<jlong>(handler);
-}
-
-static void nativeSendMessage(JNIEnv* env, jclass clazz, jlong handlerPtr, jint deviceType,
- jint dstAddr, jint opcode, jbyteArray params) {
- HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
- cec_logical_address_t srcAddr = handler->getLogicalAddress(
- static_cast<cec_device_type_t>(deviceType));
- jsize len = env->GetArrayLength(params);
- ScopedByteArrayRO paramsPtr(env, params);
- cec_message_t message;
- message.initiator = srcAddr;
- message.destination = static_cast<cec_logical_address_t>(dstAddr);
- message.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH);
- message.body[0] = opcode;
- std::memcpy(message.body + 1, paramsPtr.get(), message.length - 1);
- handler->sendCecMessage(message);
-}
-
-static jint nativeAllocateLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
- jint deviceType) {
- HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
- return handler->initLogicalDevice(static_cast<cec_device_type_t>(deviceType));
-}
-
-static void nativeRemoveLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
- jint deviceType) {
- HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
- return handler->releaseLogicalDevice(static_cast<cec_device_type_t>(deviceType));
-}
-
-static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr) {
- HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
- return handler->getPhysicalAddress();
-}
-
-static void nativeSetOsdName(JNIEnv* env, jclass clazz, jlong handlerPtr, jbyteArray name) {
- HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
- jsize len = env->GetArrayLength(name);
- if (len > 0) {
- ScopedByteArrayRO namePtr(env, name);
- handler->setOsdName(reinterpret_cast<const char *>(namePtr.get()), len);
- }
-}
-
-static JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecService;)J",
- (void *)nativeInit },
- { "nativeSendMessage", "(JIII[B)V",
- (void *)nativeSendMessage },
- { "nativeAllocateLogicalAddress", "(JI)I",
- (void *)nativeAllocateLogicalAddress },
- { "nativeRemoveLogicalAddress", "(JI)V",
- (void *)nativeRemoveLogicalAddress },
- { "nativeGetPhysicalAddress", "(J)I",
- (void *)nativeGetPhysicalAddress },
- { "nativeSetOsdName", "(J[B)V",
- (void *)nativeSetOsdName },
-};
-
-#define CLASS_PATH "com/android/server/hdmi/HdmiCecService"
-
-int register_android_server_hdmi_HdmiCecService(JNIEnv* env) {
- int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
- LOG_FATAL_IF(res < 0, "Unable to register native methods.");
- return 0;
-}
-
-} /* namespace android */
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
index 6c14887..c2fccc1 100644
--- a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -139,6 +139,8 @@
* the HW module and obtaining the proper interfaces.
*/
static void ClassInit(JNIEnv* env, jclass clazz) {
+ sFlpInterface = NULL;
+
// get references to the Java provider methods
sOnLocationReport = env->GetMethodID(
clazz,
@@ -163,6 +165,38 @@
sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V");
sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V");
sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V");
+
+ // open the hardware module
+ const hw_module_t* module = NULL;
+ int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
+ if (err != 0) {
+ ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
+ return;
+ }
+
+ err = module->methods->open(
+ module,
+ FUSED_LOCATION_HARDWARE_MODULE_ID,
+ &sHardwareDevice);
+ if (err != 0) {
+ ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
+ return;
+ }
+
+ // acquire the interfaces pointers
+ flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
+ sFlpInterface = flp_device->get_flp_interface(flp_device);
+
+ if (sFlpInterface != NULL) {
+ sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
+ sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE));
+
+ sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
+ sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE));
+
+ sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
+ sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE));
+ }
}
/*
@@ -637,44 +671,6 @@
* the Flp interfaces are initialized properly.
*/
static void Init(JNIEnv* env, jobject obj) {
- if(sHardwareDevice != NULL) {
- ALOGD("Hardware Device already opened.");
- return;
- }
-
- const hw_module_t* module = NULL;
- int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
- if(err != 0) {
- ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
- return;
- }
-
- err = module->methods->open(
- module,
- FUSED_LOCATION_HARDWARE_MODULE_ID, &sHardwareDevice);
- if(err != 0) {
- ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
- return;
- }
-
- sFlpInterface = NULL;
- flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
- sFlpInterface = flp_device->get_flp_interface(flp_device);
-
- if(sFlpInterface != NULL) {
- sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
- sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE)
- );
-
- sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
- sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE)
- );
-
- sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
- sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE)
- );
- }
-
if(sCallbacksObj == NULL) {
sCallbacksObj = env->NewGlobalRef(obj);
}
@@ -696,7 +692,10 @@
}
static jboolean IsSupported(JNIEnv* env, jclass clazz) {
- return sFlpInterface != NULL;
+ if (sFlpInterface == NULL) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
}
static jint GetBatchSize(JNIEnv* env, jobject object) {
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index e9ba116..5bafb52 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -30,6 +30,8 @@
#include <string.h>
#include <pthread.h>
+#include <linux/in.h>
+#include <linux/in6.h>
static jobject mCallbacksObj = NULL;
@@ -168,19 +170,98 @@
create_thread_callback,
};
+static jbyteArray convert_to_ipv4(uint32_t ip, bool net_order)
+{
+ if (INADDR_NONE == ip) {
+ return NULL;
+ }
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jbyteArray byteArray = env->NewByteArray(4);
+ if (byteArray == NULL) {
+ ALOGE("Unable to allocate byte array for IPv4 address");
+ return NULL;
+ }
+
+ jbyte ipv4[4];
+ if (net_order) {
+ memcpy(ipv4, &ip, sizeof(ipv4));
+ } else {
+ //endianess transparent conversion from int to char[]
+ ipv4[0] = (jbyte) (ip & 0xFF);
+ ipv4[1] = (jbyte)((ip>>8) & 0xFF);
+ ipv4[2] = (jbyte)((ip>>16) & 0xFF);
+ ipv4[3] = (jbyte) (ip>>24);
+ }
+
+ env->SetByteArrayRegion(byteArray, 0, 4, (const jbyte*) ipv4);
+ return byteArray;
+}
+
static void agps_status_callback(AGpsStatus* agps_status)
{
JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jbyteArray byteArray = NULL;
+ bool isSupported = false;
- uint32_t ipaddr;
- // ipaddr field was not included in original AGpsStatus
- if (agps_status->size >= sizeof(AGpsStatus))
- ipaddr = agps_status->ipaddr;
- else
- ipaddr = 0xFFFFFFFF;
- env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus,
- agps_status->type, agps_status->status, ipaddr);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ size_t status_size = agps_status->size;
+ if (status_size == sizeof(AGpsStatus_v3)) {
+ switch (agps_status->addr.ss_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *in = (struct sockaddr_in*)&(agps_status->addr);
+ uint32_t *pAddr = (uint32_t*)&(in->sin_addr);
+ byteArray = convert_to_ipv4(*pAddr, true /* net_order */);
+ if (byteArray != NULL) {
+ isSupported = true;
+ }
+ }
+ break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(agps_status->addr);
+ byteArray = env->NewByteArray(16);
+ if (byteArray != NULL) {
+ env->SetByteArrayRegion(byteArray, 0, 16, (const jbyte *)&(in6->sin6_addr));
+ isSupported = true;
+ } else {
+ ALOGE("Unable to allocate byte array for IPv6 address.");
+ }
+ }
+ break;
+ default:
+ ALOGE("Invalid ss_family found: %d", agps_status->addr.ss_family);
+ break;
+ }
+ } else if (status_size >= sizeof(AGpsStatus_v2)) {
+ // for back-compatibility reasons we check in v2 that the data structure size is greater or
+ // equal to the declared size in gps.h
+ uint32_t ipaddr = agps_status->ipaddr;
+ byteArray = convert_to_ipv4(ipaddr, false /* net_order */);
+ if (ipaddr == INADDR_NONE || byteArray != NULL) {
+ isSupported = true;
+ }
+ } else if (status_size >= sizeof(AGpsStatus_v1)) {
+ // because we have to check for >= with regards to v2, we also need to relax the check here
+ // and only make sure that the size is at least what we expect
+ isSupported = true;
+ } else {
+ ALOGE("Invalid size of AGpsStatus found: %d.", status_size);
+ }
+
+ if (isSupported) {
+ env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, agps_status->type,
+ agps_status->status, byteArray);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ } else {
+ ALOGD("Skipping calling method_reportAGpsStatus.");
+ }
+
+ if (byteArray) {
+ env->DeleteLocalRef(byteArray);
+ }
}
AGpsCallbacks sAGpsCallbacks = {
@@ -339,7 +420,7 @@
method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
- method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(III)V");
+ method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II[B)V");
method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
@@ -610,7 +691,8 @@
env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT);
}
-static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn)
+static void android_location_GpsLocationProvider_agps_data_conn_open(
+ JNIEnv* env, jobject obj, jstring apn, jint apnIpType)
{
if (!sAGpsInterface) {
ALOGE("no AGPS interface in agps_data_conn_open");
@@ -620,8 +702,18 @@
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return;
}
+
const char *apnStr = env->GetStringUTFChars(apn, NULL);
- sAGpsInterface->data_conn_open(apnStr);
+
+ size_t interface_size = sAGpsInterface->size;
+ if (interface_size == sizeof(AGpsInterface_v2)) {
+ sAGpsInterface->data_conn_open_with_apn_ip_type(apnStr, apnIpType);
+ } else if (interface_size == sizeof(AGpsInterface_v1)) {
+ sAGpsInterface->data_conn_open(apnStr);
+ } else {
+ ALOGE("Invalid size of AGpsInterface found: %d.", interface_size);
+ }
+
env->ReleaseStringUTFChars(apn, apnStr);
}
@@ -775,7 +867,7 @@
{"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location},
{"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra},
{"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data},
- {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open},
+ {"native_agps_data_conn_open", "(Ljava/lang/String;I)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open},
{"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed},
{"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed},
{"native_agps_set_id","(ILjava/lang/String;)V",(void*)android_location_GpsLocationProvider_agps_set_id},
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index f0c4f3a..afe629d 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -316,7 +316,7 @@
(void*) nativeOpen },
{ "nativeSetSurface", "(JIILandroid/view/Surface;)I",
(void*) nativeSetSurface },
- { "nativeGetStreamConfigs", "(JII)[Landroid/tv/TvStreamConfig;",
+ { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
(void*) nativeGetStreamConfigs },
{ "nativeClose", "(J)V",
(void*) nativeClose },
@@ -346,10 +346,10 @@
gTvInputHalClassInfo.streamConfigsChanged, clazz,
"streamConfigsChangedFromNative", "(I)V");
- FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/tv/TvStreamConfig");
+ FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
- FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/tv/TvStreamConfig$Builder");
+ FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
gTvStreamConfigBuilderClassInfo.clazz =
jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
@@ -360,27 +360,27 @@
GET_METHOD_ID(
gTvStreamConfigBuilderClassInfo.streamId,
gTvStreamConfigBuilderClassInfo.clazz,
- "streamId", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
GET_METHOD_ID(
gTvStreamConfigBuilderClassInfo.type,
gTvStreamConfigBuilderClassInfo.clazz,
- "type", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
GET_METHOD_ID(
gTvStreamConfigBuilderClassInfo.maxWidth,
gTvStreamConfigBuilderClassInfo.clazz,
- "maxWidth", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
GET_METHOD_ID(
gTvStreamConfigBuilderClassInfo.maxHeight,
gTvStreamConfigBuilderClassInfo.clazz,
- "maxHeight", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
GET_METHOD_ID(
gTvStreamConfigBuilderClassInfo.generation,
gTvStreamConfigBuilderClassInfo.clazz,
- "generation", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
GET_METHOD_ID(
gTvStreamConfigBuilderClassInfo.build,
gTvStreamConfigBuilderClassInfo.clazz,
- "build", "()Landroid/tv/TvStreamConfig;");
+ "build", "()Landroid/media/tv/TvStreamConfig;");
return 0;
}
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index bfa8286..a302104 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -39,7 +39,6 @@
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_dreams_McuHal(JNIEnv* env);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
-int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
};
@@ -76,8 +75,6 @@
register_android_server_dreams_McuHal(env);
register_android_server_BatteryStatsService(env);
register_android_server_hdmi_HdmiCecController(env);
- // TODO: remove this once replaces HdmiCecService with HdmiControlService.
- register_android_server_hdmi_HdmiCecService(env);
register_android_server_hdmi_HdmiMhlController(env);
register_android_server_tv_TvInputHal(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
index 674c6f4..f1284d80 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
@@ -120,6 +120,10 @@
mDeviceOwner = new OwnerInfo(ownerName, packageName);
}
+ void clearDeviceOwner() {
+ mDeviceOwner = null;
+ }
+
void setProfileOwner(String packageName, String ownerName, int userId) {
mProfileOwners.put(userId, new OwnerInfo(ownerName, packageName));
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a52396e..4574caf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -32,6 +32,7 @@
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppGlobals;
+import android.app.IActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -51,6 +52,10 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.media.AudioManager;
+import android.media.IAudioService;
+import android.net.ConnectivityManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.net.ProxyInfo;
import android.os.Binder;
import android.os.Bundle;
@@ -814,12 +819,20 @@
}
if (who != null) {
+ if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
+ throw new SecurityException("Admin " + candidates.get(0).info.getComponent()
+ + " does not own the device");
+ }
+ if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
+ throw new SecurityException("Admin " + candidates.get(0).info.getComponent()
+ + " does not own the profile");
+ }
throw new SecurityException("Admin " + candidates.get(0).info.getComponent()
+ " did not specify uses-policy for: "
+ candidates.get(0).info.getTagForPolicy(reqPolicy));
} else {
throw new SecurityException("No active admin owned by uid "
- + Binder.getCallingUid() + " for policy:" + reqPolicy);
+ + Binder.getCallingUid() + " for policy #" + reqPolicy);
}
}
@@ -827,20 +840,28 @@
sendAdminCommandLocked(admin, action, null);
}
+ void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) {
+ sendAdminCommandLocked(admin, action, null, result);
+ }
+
/**
* Send an update to one specific admin, get notified when that admin returns a result.
*/
- void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) {
+ void sendAdminCommandLocked(ActiveAdmin admin, String action, Bundle adminExtras,
+ BroadcastReceiver result) {
Intent intent = new Intent(action);
intent.setComponent(admin.info.getComponent());
if (action.equals(DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING)) {
intent.putExtra("expiration", admin.passwordExpirationDate);
}
+ if (adminExtras != null) {
+ intent.putExtras(adminExtras);
+ }
if (result != null) {
mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(),
null, result, mHandler, Activity.RESULT_OK, null, null);
} else {
- mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+ mContext.sendBroadcastAsUser(intent, admin.getUserHandle());
}
}
@@ -1227,6 +1248,7 @@
loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER);
loadDeviceOwner();
}
+ cleanUpOldUsers();
mAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
if (mDeviceOwner != null) {
@@ -1247,6 +1269,32 @@
}
}
+ private void cleanUpOldUsers() {
+ // This is needed in case the broadcast {@link Intent.ACTION_USER_REMOVED} was not handled
+ // before reboot
+ Set<Integer> usersWithProfileOwners;
+ Set<Integer> usersWithData;
+ synchronized(this) {
+ usersWithProfileOwners = mDeviceOwner != null
+ ? mDeviceOwner.getProfileOwnerKeys() : new HashSet<Integer>();
+ usersWithData = new HashSet<Integer>();
+ for (int i = 0; i < mUserData.size(); i++) {
+ usersWithData.add(mUserData.keyAt(i));
+ }
+ }
+ List<UserInfo> allUsers = mUserManager.getUsers();
+
+ Set<Integer> deletedUsers = new HashSet<Integer>();
+ deletedUsers.addAll(usersWithProfileOwners);
+ deletedUsers.addAll(usersWithData);
+ for (UserInfo userInfo : allUsers) {
+ deletedUsers.remove(userInfo.id);
+ }
+ for (Integer userId : deletedUsers) {
+ removeUserData(userId);
+ }
+ }
+
private void handlePasswordExpirationNotification(int userHandle) {
synchronized (this) {
final long now = System.currentTimeMillis();
@@ -1336,6 +1384,11 @@
if (!mHasFeature) {
return;
}
+ setActiveAdmin(adminReceiver, refreshing, userHandle, null);
+ }
+
+ private void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle,
+ Bundle onEnableData) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
enforceCrossUserPermission(userHandle);
@@ -1372,7 +1425,8 @@
policy.mAdminList.set(replaceIndex, newAdmin);
}
saveSettingsLocked(userHandle);
- sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED);
+ sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED,
+ onEnableData, null);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2695,6 +2749,20 @@
return null;
}
+ public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) {
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ ConnectivityManager connectivityManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ connectivityManager.setGlobalProxy(proxyInfo);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private void resetGlobalProxyLocked(DevicePolicyData policy) {
final int N = policy.mAdminList.size();
for (int i = 0; i < N; i++) {
@@ -3056,6 +3124,37 @@
}
@Override
+ public void clearDeviceOwner(String packageName) {
+ if (packageName == null) {
+ throw new NullPointerException("packageName is null");
+ }
+ try {
+ int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
+ if (uid != Binder.getCallingUid()) {
+ throw new SecurityException("Invalid packageName");
+ }
+ } catch (NameNotFoundException e) {
+ throw new SecurityException(e);
+ }
+ if (!isDeviceOwner(packageName)) {
+ throw new SecurityException("clearDeviceOwner can only be called by the device owner");
+ }
+ synchronized (this) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mUserManager.setUserRestrictions(new Bundle(),
+ new UserHandle(UserHandle.USER_OWNER));
+ if (mDeviceOwner != null) {
+ mDeviceOwner.clearDeviceOwner();
+ mDeviceOwner.writeOwnerFile();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
public boolean setProfileOwner(String packageName, String ownerName, int userHandle) {
if (!mHasFeature) {
return false;
@@ -3123,6 +3222,7 @@
intent.putExtra(Intent.EXTRA_USER, new UserHandle(UserHandle.getCallingUserId()));
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
Intent.FLAG_RECEIVER_FOREGROUND);
+ // TODO This should send to parent of profile (which is always owner at the moment).
mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
} finally {
restoreCallingIdentity(id);
@@ -3413,6 +3513,36 @@
}
@Override
+ public UserHandle createAndInitializeUser(ComponentName who, String name,
+ String ownerName, ComponentName profileOwnerComponent, Bundle adminExtras) {
+ UserHandle user = createUser(who, name);
+ long id = Binder.clearCallingIdentity();
+ try {
+ String profileOwnerPkg = profileOwnerComponent.getPackageName();
+ final IPackageManager ipm = AppGlobals.getPackageManager();
+ IActivityManager activityManager = ActivityManagerNative.getDefault();
+
+ try {
+ // Install the profile owner if not present.
+ if (!ipm.isPackageAvailable(profileOwnerPkg, user.getIdentifier())) {
+ ipm.installExistingPackageAsUser(profileOwnerPkg, user.getIdentifier());
+ }
+
+ // Start user in background.
+ activityManager.startUserInBackground(user.getIdentifier());
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Failed to make remote calls for configureUser", e);
+ }
+
+ setActiveAdmin(profileOwnerComponent, true, user.getIdentifier(), adminExtras);
+ setProfileOwner(profileOwnerPkg, ownerName, user.getIdentifier());
+ return user;
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+
+ @Override
public boolean removeUser(ComponentName who, UserHandle userHandle) {
synchronized (this) {
if (who == null) {
@@ -3555,111 +3685,6 @@
}
@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) {
@@ -3805,4 +3830,40 @@
}
}
}
+
+ @Override
+ public void setMasterVolumeMuted(ComponentName who, boolean on) {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ IAudioService iAudioService = IAudioService.Stub.asInterface(
+ ServiceManager.getService(Context.AUDIO_SERVICE));
+ try{
+ iAudioService.setMasterMute(on, 0, who.getPackageName(), null);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Failed to setMasterMute", re);
+ }
+ }
+ }
+
+ @Override
+ public boolean isMasterVolumeMuted(ComponentName who) {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+ AudioManager audioManager =
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ return audioManager.isMasterMute();
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 164fe05..3102cce 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -29,6 +29,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.media.AudioService;
+import android.media.tv.TvInputManager;
import android.os.Build;
import android.os.Environment;
import android.os.FactoryTest;
@@ -43,7 +44,6 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.service.dreams.DreamService;
-import android.tv.TvInputManager;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -65,6 +65,7 @@
import com.android.server.dreams.DreamManagerService;
import com.android.server.hdmi.HdmiControlService;
import com.android.server.input.InputManagerService;
+import com.android.server.job.JobSchedulerService;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LightsService;
import com.android.server.media.MediaRouterService;
@@ -84,7 +85,6 @@
import com.android.server.search.SearchManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
-import com.android.server.task.TaskManagerService;
import com.android.server.trust.TrustManagerService;
import com.android.server.tv.TvInputManagerService;
import com.android.server.twilight.TwilightService;
@@ -130,12 +130,10 @@
"com.android.server.wifi.passpoint.WifiPasspointService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
- private static final String HDMI_CEC_SERVICE_CLASS =
- "com.android.server.hdmi.HdmiCecService";
private static final String ETHERNET_SERVICE_CLASS =
"com.android.server.ethernet.EthernetService";
- private static final String TASK_SERVICE_CLASS =
- "com.android.server.task.TaskManagerService";
+ private static final String JOB_SCHEDULER_SERVICE_CLASS =
+ "com.android.server.job.JobSchedulerService";
private final int mFactoryTestMode;
private Timer mProfilerSnapshotTimer;
@@ -351,6 +349,7 @@
boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
+ boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
try {
Slog.i(TAG, "Telephony Registry");
@@ -463,7 +462,7 @@
// Skip Bluetooth if we have an emulator kernel
// TODO: Use a more reliable check to see if this product should
// support Bluetooth - see bug 988521
- if (SystemProperties.get("ro.kernel.qemu").equals("1")) {
+ if (isEmulator) {
Slog.i(TAG, "No Bluetooh Service (emulator)");
} else if (mFactoryTestMode == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
Slog.i(TAG, "No Bluetooth Service (factory test)");
@@ -664,10 +663,17 @@
reportWtf("starting Wi-Fi Scanning Service", e);
}
- try {
- mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting Ethernet Service", e);
+ if (!isEmulator) {
+ try {
+ mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting Ethernet Service", e);
+ }
+ } else {
+ // Don't start the Ethernet service on the emulator because
+ // it interferes with qemu's SLIRP emulation, which uses
+ // IPv4 over eth0. http://b/15341003 .
+ Slog.i(TAG, "Not starting Ethernet service (emulator)");
}
try {
@@ -828,7 +834,7 @@
mSystemServiceManager.startService(UiModeManagerService.class);
- mSystemServiceManager.startService(TaskManagerService.class);
+ mSystemServiceManager.startService(JobSchedulerService.class);
if (!disableNonCoreServices) {
try {
@@ -954,12 +960,6 @@
}
try {
- mSystemServiceManager.startService(HDMI_CEC_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting HdmiCec Service", e);
- }
-
- try {
mSystemServiceManager.startService(HdmiControlService.class);
} catch (Throwable e) {
reportWtf("starting HdmiControlService", e);
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 1bb61d2..a8c739c 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -121,15 +121,7 @@
throwIfDestroyed();
// Stop tracking printers.
- if (mTrackedPrinterList != null) {
- final int trackedPrinterCount = mTrackedPrinterList.size();
- for (int i = 0; i < trackedPrinterCount; i++) {
- PrinterId printerId = mTrackedPrinterList.get(i);
- if (printerId.getServiceName().equals(mComponentName)) {
- handleStopPrinterStateTracking(printerId);
- }
- }
- }
+ stopTrackingAllPrinters();
// Stop printer discovery.
if (mDiscoveryPriorityList != null) {
@@ -270,7 +262,7 @@
try {
mPrintService.createPrinterDiscoverySession();
} catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error creating printer dicovery session.", re);
+ Slog.e(LOG_TAG, "Error creating printer discovery session.", re);
}
}
}
@@ -365,10 +357,14 @@
if (DEBUG) {
Slog.i(LOG_TAG, "[user: " + mUserId + "] stopPrinterDiscovery()");
}
+
+ // Stop tracking printers.
+ stopTrackingAllPrinters();
+
try {
mPrintService.stopPrinterDiscovery();
} catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error stopping printer dicovery.", re);
+ Slog.e(LOG_TAG, "Error stopping printer discovery.", re);
}
}
}
@@ -466,6 +462,19 @@
}
}
+ private void stopTrackingAllPrinters() {
+ if (mTrackedPrinterList == null) {
+ return;
+ }
+ final int trackedPrinterCount = mTrackedPrinterList.size();
+ for (int i = trackedPrinterCount - 1; i >= 0; i--) {
+ PrinterId printerId = mTrackedPrinterList.get(i);
+ if (printerId.getServiceName().equals(mComponentName)) {
+ handleStopPrinterStateTracking(printerId);
+ }
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
String tab = " ";
pw.append(prefix).append("service:").println();
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index ffe9806..9496cae 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -44,7 +44,7 @@
/**
* This represents the remote print spooler as a local object to the
- * PrintManagerSerivce. It is responsible to connecting to the remote
+ * PrintManagerService. It is responsible to connecting to the remote
* spooler if needed, to make the timed remote calls, to handle
* remote exceptions, and to bind/unbind to the remote instance as
* needed.
@@ -99,7 +99,7 @@
mClient = new PrintSpoolerClient(this);
mIntent = new Intent();
mIntent.setComponent(new ComponentName("com.android.printspooler",
- "com.android.printspooler.PrintSpoolerService"));
+ "com.android.printspooler.model.PrintSpoolerService"));
}
public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
diff --git a/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java b/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java
new file mode 100644
index 0000000..7a7fa07
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/task/TaskStoreTest.java
@@ -0,0 +1,200 @@
+package com.android.server.task;
+
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.app.job.JobInfo;
+import android.app.job.JobInfo.Builder;
+import android.os.PersistableBundle;
+import android.test.AndroidTestCase;
+import android.test.RenamingDelegatingContext;
+import android.util.Log;
+
+import com.android.server.job.JobMapReadFinishedListener;
+import com.android.server.job.JobStore;
+import com.android.server.job.controllers.JobStatus;
+
+import java.util.List;
+
+/**
+ * Test reading and writing correctly from file.
+ */
+public class TaskStoreTest extends AndroidTestCase {
+ private static final String TAG = "TaskStoreTest";
+ private static final String TEST_PREFIX = "_test_";
+ // private static final int USER_NON_0 = 3;
+ private static final int SOME_UID = 34234;
+ private ComponentName mComponent;
+ private static final long IO_WAIT = 600L;
+
+ JobStore mTaskStoreUnderTest;
+ Context mTestContext;
+ JobMapReadFinishedListener mTaskMapReadFinishedListenerStub =
+ new JobMapReadFinishedListener() {
+ @Override
+ public void onJobMapReadFinished(List<JobStatus> tasks) {
+ // do nothing.
+ }
+ };
+
+ @Override
+ public void setUp() throws Exception {
+ mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX);
+ Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'");
+ mTaskStoreUnderTest = JobStore.initAndGetForTesting(mTestContext,
+ mTestContext.getFilesDir(), mTaskMapReadFinishedListenerStub);
+ mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName());
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mTaskStoreUnderTest.clear();
+ }
+
+ public void testMaybeWriteStatusToDisk() throws Exception {
+ int taskId = 5;
+ long runByMillis = 20000L; // 20s
+ long runFromMillis = 2000L; // 2s
+ long initialBackoff = 10000L; // 10s
+
+ final JobInfo task = new Builder(taskId, mComponent)
+ .setRequiresCharging(true)
+ .setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY)
+ .setBackoffCriteria(initialBackoff, JobInfo.BackoffPolicy.EXPONENTIAL)
+ .setOverrideDeadline(runByMillis)
+ .setMinimumLatency(runFromMillis)
+ .build();
+ final JobStatus ts = new JobStatus(task, SOME_UID, true /* persisted */);
+ mTaskStoreUnderTest.add(ts);
+ Thread.sleep(IO_WAIT);
+ // Manually load tasks from xml file.
+ mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
+ @Override
+ public void onJobMapReadFinished(List<JobStatus> tasks) {
+ assertEquals("Didn't get expected number of persisted tasks.", 1, tasks.size());
+ JobStatus loadedTaskStatus = tasks.get(0);
+ assertTasksEqual(task, loadedTaskStatus.getJob());
+ assertEquals("Different uids.", SOME_UID, tasks.get(0).getUid());
+ compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
+ ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
+ compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
+ ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed());
+ }
+ });
+
+ }
+
+ public void testWritingTwoFilesToDisk() throws Exception {
+ final JobInfo task1 = new Builder(8, mComponent)
+ .setRequiresDeviceIdle(true)
+ .setPeriodic(10000L)
+ .setRequiresCharging(true)
+ .build();
+ final JobInfo task2 = new Builder(12, mComponent)
+ .setMinimumLatency(5000L)
+ .setBackoffCriteria(15000L, JobInfo.BackoffPolicy.LINEAR)
+ .setOverrideDeadline(30000L)
+ .setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED)
+ .build();
+ final JobStatus taskStatus1 = new JobStatus(task1, SOME_UID, true /* persisted */);
+ final JobStatus taskStatus2 = new JobStatus(task2, SOME_UID, true /* persisted */);
+ mTaskStoreUnderTest.add(taskStatus1);
+ mTaskStoreUnderTest.add(taskStatus2);
+ Thread.sleep(IO_WAIT);
+ mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
+ @Override
+ public void onJobMapReadFinished(List<JobStatus> tasks) {
+ assertEquals("Incorrect # of persisted tasks.", 2, tasks.size());
+ JobStatus loaded1 = tasks.get(0);
+ JobStatus loaded2 = tasks.get(1);
+ assertTasksEqual(task1, loaded1.getJob());
+ assertTasksEqual(task2, loaded2.getJob());
+
+ // Check that the loaded task has the correct runtimes.
+ compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
+ taskStatus1.getEarliestRunTime(), loaded1.getEarliestRunTime());
+ compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
+ taskStatus1.getLatestRunTimeElapsed(), loaded1.getLatestRunTimeElapsed());
+ compareTimestampsSubjectToIoLatency("Early run-times not the same after read.",
+ taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime());
+ compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
+ taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed());
+ }
+ });
+
+ }
+
+ public void testWritingTaskWithExtras() throws Exception {
+ JobInfo.Builder b = new Builder(8, mComponent)
+ .setRequiresDeviceIdle(true)
+ .setPeriodic(10000L)
+ .setRequiresCharging(true);
+
+ PersistableBundle extras = new PersistableBundle();
+ extras.putDouble("hello", 3.2);
+ extras.putString("hi", "there");
+ extras.putInt("into", 3);
+ b.setExtras(extras);
+ final JobInfo task = b.build();
+ JobStatus taskStatus = new JobStatus(task, SOME_UID, true /* persisted */);
+
+ mTaskStoreUnderTest.add(taskStatus);
+ Thread.sleep(IO_WAIT);
+ mTaskStoreUnderTest.readJobMapFromDisk(new JobMapReadFinishedListener() {
+ @Override
+ public void onJobMapReadFinished(List<JobStatus> tasks) {
+ assertEquals("Incorrect # of persisted tasks.", 1, tasks.size());
+ JobStatus loaded = tasks.get(0);
+ assertTasksEqual(task, loaded.getJob());
+ }
+ });
+
+ }
+
+ /**
+ * Helper function to throw an error if the provided task and TaskStatus objects are not equal.
+ */
+ private void assertTasksEqual(JobInfo first, JobInfo second) {
+ assertEquals("Different task ids.", first.getId(), second.getId());
+ assertEquals("Different components.", first.getService(), second.getService());
+ assertEquals("Different periodic status.", first.isPeriodic(), second.isPeriodic());
+ assertEquals("Different period.", first.getIntervalMillis(), second.getIntervalMillis());
+ assertEquals("Different inital backoff.", first.getInitialBackoffMillis(),
+ second.getInitialBackoffMillis());
+ assertEquals("Different backoff policy.", first.getBackoffPolicy(),
+ second.getBackoffPolicy());
+
+ assertEquals("Invalid charging constraint.", first.isRequireCharging(),
+ second.isRequireCharging());
+ assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(),
+ second.isRequireDeviceIdle());
+ assertEquals("Invalid unmetered constraint.",
+ first.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED,
+ second.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED);
+ assertEquals("Invalid connectivity constraint.",
+ first.getNetworkCapabilities() == JobInfo.NetworkType.ANY,
+ second.getNetworkCapabilities() == JobInfo.NetworkType.ANY);
+ assertEquals("Invalid deadline constraint.",
+ first.hasLateConstraint(),
+ second.hasLateConstraint());
+ assertEquals("Invalid delay constraint.",
+ first.hasEarlyConstraint(),
+ second.hasEarlyConstraint());
+ assertEquals("Extras don't match",
+ first.getExtras().toString(), second.getExtras().toString());
+ }
+
+ /**
+ * When comparing timestamps before and after DB read/writes (to make sure we're saving/loading
+ * the correct values), there is some latency involved that terrorises a naive assertEquals().
+ * We define a <code>DELTA_MILLIS</code> as a function variable here to make this comparision
+ * more reasonable.
+ */
+ private void compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2) {
+ final long DELTA_MILLIS = 700L; // We allow up to 700ms of latency for IO read/writes.
+ assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS + IO_WAIT);
+ }
+
+ private static class StubClass {}
+
+}
\ No newline at end of file
diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
index 0946c5a..cc5d004 100644
--- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
@@ -84,7 +84,6 @@
while (true) {
int count = inputStream.read(buffer);
if (count < 0) {
- Slog.e(TAG, "got " + count + " reading");
break;
}
@@ -100,9 +99,6 @@
break;
}
}
- } catch (IOException ex) {
- Slog.e(TAG, "Communication error: ", ex);
- throw ex;
} finally {
closeSocket();
}
diff --git a/telecomm/java/android/telecomm/CallCapabilities.java b/telecomm/java/android/telecomm/CallCapabilities.java
index b2b33a3..5aff19c 100644
--- a/telecomm/java/android/telecomm/CallCapabilities.java
+++ b/telecomm/java/android/telecomm/CallCapabilities.java
@@ -17,7 +17,7 @@
package android.telecomm;
/** Defines actions a call currently supports. */
-public class CallCapabilities {
+public final class CallCapabilities {
/** Call can currently be put on hold or unheld. */
public static final int HOLD = 0x00000001;
@@ -27,24 +27,60 @@
/** Call can currently be merged. */
public static final int MERGE_CALLS = 0x00000004;
- /* Call can currently be swapped with another call. */
+ /** Call can currently be swapped with another call. */
public static final int SWAP_CALLS = 0x00000008;
- /* Call currently supports adding another call to this one. */
+ /** Call currently supports adding another call to this one. */
public static final int ADD_CALL = 0x00000010;
- /* Call supports responding via text option. */
+ /** Call supports responding via text option. */
public static final int RESPOND_VIA_TEXT = 0x00000020;
- /* Call can be muted. */
+ /** Call can be muted. */
public static final int MUTE = 0x00000040;
- /* Call supports generic conference mode. */
+ /** Call supports generic conference mode. */
public static final int GENERIC_CONFERENCE = 0x00000080;
- /* Call currently supports switch between connections. */
+ /** Call currently supports switch between connections. */
public static final int CONNECTION_HANDOFF = 0x00000100;
public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CALLS | SWAP_CALLS | ADD_CALL
| RESPOND_VIA_TEXT | MUTE | GENERIC_CONFERENCE | CONNECTION_HANDOFF;
+
+ public static String toString(int capabilities) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[Capabilities:");
+ if ((capabilities & HOLD) != 0) {
+ builder.append(" HOLD");
+ }
+ if ((capabilities & SUPPORT_HOLD) != 0) {
+ builder.append(" SUPPORT_HOLD");
+ }
+ if ((capabilities & MERGE_CALLS) != 0) {
+ builder.append(" MERGE_CALLS");
+ }
+ if ((capabilities & SWAP_CALLS) != 0) {
+ builder.append(" SWAP_CALLS");
+ }
+ if ((capabilities & ADD_CALL) != 0) {
+ builder.append(" ADD_CALL");
+ }
+ if ((capabilities & RESPOND_VIA_TEXT) != 0) {
+ builder.append(" RESPOND_VIA_TEXT");
+ }
+ if ((capabilities & MUTE) != 0) {
+ builder.append(" MUTE");
+ }
+ if ((capabilities & GENERIC_CONFERENCE) != 0) {
+ builder.append(" GENERIC_CONFERENCE");
+ }
+ if ((capabilities & CONNECTION_HANDOFF) != 0) {
+ builder.append(" HANDOFF");
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+ private CallCapabilities() {}
}
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
index d452172..0b5981c 100644
--- a/telecomm/java/android/telecomm/CallService.java
+++ b/telecomm/java/android/telecomm/CallService.java
@@ -61,8 +61,9 @@
private static final int MSG_ON_AUDIO_STATE_CHANGED = 11;
private static final int MSG_PLAY_DTMF_TONE = 12;
private static final int MSG_STOP_DTMF_TONE = 13;
- private static final int MSG_ADD_TO_CONFERENCE = 14;
+ private static final int MSG_CONFERENCE = 14;
private static final int MSG_SPLIT_FROM_CONFERENCE = 15;
+ private static final int MSG_ON_POST_DIAL_CONTINUE = 16;
/**
* Default Handler used to consolidate binder method calls onto a single thread.
@@ -127,29 +128,31 @@
case MSG_STOP_DTMF_TONE:
stopDtmfTone((String) msg.obj);
break;
- case MSG_ADD_TO_CONFERENCE: {
- SomeArgs args = (SomeArgs) msg.obj;
- try {
- @SuppressWarnings("unchecked")
- List<String> callIds = (List<String>) args.arg2;
- String conferenceCallId = (String) args.arg1;
- addToConference(conferenceCallId, callIds);
- } finally {
- args.recycle();
- }
- break;
- }
- case MSG_SPLIT_FROM_CONFERENCE: {
+ case MSG_CONFERENCE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
String conferenceCallId = (String) args.arg1;
String callId = (String) args.arg2;
- splitFromConference(conferenceCallId, callId);
+ conference(conferenceCallId, callId);
} finally {
args.recycle();
}
break;
}
+ case MSG_ON_POST_DIAL_CONTINUE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String callId = (String) args.arg1;
+ boolean proceed = (args.argi1 == 1);
+ onPostDialContinue(callId, proceed);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SPLIT_FROM_CONFERENCE:
+ splitFromConference((String) msg.obj);
+ break;
default:
break;
}
@@ -233,19 +236,24 @@
}
@Override
- public void addToConference(String conferenceCallId, List<String> callsToConference) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = conferenceCallId;
- args.arg2 = callsToConference;
- mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget();
- }
-
- @Override
- public void splitFromConference(String conferenceCallId, String callId) {
+ public void conference(String conferenceCallId, String callId) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = conferenceCallId;
args.arg2 = callId;
- mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget();
+ mMessageHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
+ }
+
+ @Override
+ public void splitFromConference(String callId) {
+ mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, callId).sendToTarget();
+ }
+
+ @Override
+ public void onPostDialContinue(String callId, boolean proceed) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.argi1 = proceed ? 1 : 0;
+ mMessageHandler.obtainMessage(MSG_ON_POST_DIAL_CONTINUE, args).sendToTarget();
}
}
@@ -404,22 +412,23 @@
public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState);
/**
- * Adds the specified calls to the specified conference call.
+ * Conferences the specified call.
*
* @param conferenceCallId The unique ID of the conference call onto which the specified calls
* should be added.
- * @param callIds The calls to add to the conference call.
+ * @param callId The call to conference.
* @hide
*/
- public abstract void addToConference(String conferenceCallId, List<String> callIds);
+ public abstract void conference(String conferenceCallId, String callId);
/**
- * Removes the specified call from the specified conference call. This is a no-op if the call
- * is not already part of the conference call.
+ * Removes the specified call from a conference call.
*
- * @param conferenceCallId The conference call.
* @param callId The call to remove from the conference call
* @hide
*/
- public abstract void splitFromConference(String conferenceCallId, String callId);
+ public abstract void splitFromConference(String callId);
+
+ public void onPostDialContinue(String callId, boolean proceed) {}
+ public void onPostDialWait(Connection conn, String remaining) {}
}
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index 7396808..0ba8161 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -84,12 +84,16 @@
/**
* Tells Telecomm that an attempt to place the specified outgoing call failed.
*
- * @param callId The ID of the outgoing call.
- * @param errorMessage The error associated with the failed call attempt.
+ * @param request The originating request for a connection.
+ * @param errorCode The error code associated with the failed call attempt.
+ * @param errorMsg The error message associated with the failed call attempt.
*/
- public void handleFailedOutgoingCall(String callId, String errorMessage) {
+ public void handleFailedOutgoingCall(
+ ConnectionRequest request,
+ int errorCode,
+ String errorMsg) {
try {
- mAdapter.handleFailedOutgoingCall(callId, errorMessage);
+ mAdapter.handleFailedOutgoingCall(request, errorCode, errorMsg);
} catch (RemoteException e) {
}
}
@@ -175,12 +179,12 @@
* Indicates that the specified call can conference with any of the specified list of calls.
*
* @param callId The unique ID of the call.
- * @param conferenceCapableCallIds The unique IDs of the calls which can be conferenced.
+ * @param canConference Specified whether or not the call can be conferenced.
* @hide
*/
- public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
+ public void setCanConference(String callId, boolean canConference) {
try {
- mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds);
+ mAdapter.setCanConference(callId, canConference);
} catch (RemoteException ignored) {
}
}
@@ -189,13 +193,14 @@
* Indicates whether or not the specified call is currently conferenced into the specified
* conference call.
*
- * @param conferenceCallId The unique ID of the conference call.
* @param callId The unique ID of the call being conferenced.
+ * @param conferenceCallId The unique ID of the conference call. Null if call is not
+ * conferenced.
* @hide
*/
- public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
+ public void setIsConferenced(String callId, String conferenceCallId) {
try {
- mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced);
+ mAdapter.setIsConferenced(callId, conferenceCallId);
} catch (RemoteException ignored) {
}
}
@@ -213,4 +218,35 @@
} catch (RemoteException ignored) {
}
}
+
+ public void onPostDialWait(String callId, String remaining) {
+ try {
+ mAdapter.onPostDialWait(callId, remaining);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecomm to handoff the call to another call service.
+ *
+ * @param callId The identifier of the call to handoff.
+ */
+ public void handoffCall(String callId) {
+ try {
+ mAdapter.handoffCall(callId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Indicates that a new conference call has been created.
+ *
+ * @param callId The unique ID of the conference call.
+ */
+ public void addConferenceCall(String callId) {
+ try {
+ mAdapter.addConferenceCall(callId, null);
+ } catch (RemoteException ignored) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 8cce8e6..7aee770 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -19,7 +19,10 @@
import android.net.Uri;
import android.os.Bundle;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/**
@@ -35,6 +38,8 @@
void onDisconnected(Connection c, int cause, String message);
void onRequestingRingback(Connection c, boolean ringback);
void onDestroyed(Connection c);
+ void onConferenceCapableChanged(Connection c, boolean isConferenceCapable);
+ void onParentConnectionChanged(Connection c, Connection parent);
}
public static class ListenerBase implements Listener {
@@ -65,6 +70,14 @@
/** {@inheritDoc} */
@Override
public void onRequestingRingback(Connection c, boolean ringback) {}
+
+ /** ${inheritDoc} */
+ @Override
+ public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) {}
+
+ /** ${inheritDoc} */
+ @Override
+ public void onParentConnectionChanged(Connection c, Connection parent) {}
}
public final class State {
@@ -79,10 +92,14 @@
}
private final Set<Listener> mListeners = new HashSet<>();
+ private final List<Connection> mChildConnections = new ArrayList<>();
+
private int mState = State.NEW;
private CallAudioState mCallAudioState;
private Uri mHandle;
private boolean mRequestingRingback = false;
+ private boolean mIsConferenceCapable = false;
+ private Connection mParentConnection;
/**
* Create a new Connection.
@@ -176,6 +193,16 @@
}
/**
+ * Separates this Connection from a parent connection.
+ *
+ * @hide
+ */
+ public final void separate() {
+ Log.d(this, "separate");
+ onSeparate();
+ }
+
+ /**
* Abort this Connection. The Connection will immediately transition to
* the {@link State#DISCONNECTED} state, and send no notifications of this
* or any other future events.
@@ -240,6 +267,14 @@
}
/**
+ * TODO(santoscordon): Needs updated documentation.
+ */
+ public final void conference() {
+ Log.d(this, "conference");
+ onConference();
+ }
+
+ /**
* Inform this Connection that the state of its audio output has been changed externally.
*
* @param state The new audio state.
@@ -274,7 +309,7 @@
}
/**
- * @return Whether this connection is requesting that the system play a ringback tone
+ * Returns whether this connection is requesting that the system play a ringback tone
* on its behalf.
*/
public boolean isRequestingRingback() {
@@ -282,6 +317,38 @@
}
/**
+ * Returns whether this connection is a conference connection (has child connections).
+ */
+ public boolean isConferenceConnection() {
+ return !mChildConnections.isEmpty();
+ }
+
+ public void setParentConnection(Connection parentConnection) {
+ Log.d(this, "parenting %s to %s", this, parentConnection);
+ if (mParentConnection != parentConnection) {
+ if (mParentConnection != null) {
+ mParentConnection.removeChild(this);
+ }
+ mParentConnection = parentConnection;
+ if (mParentConnection != null) {
+ mParentConnection.addChild(this);
+ // do something if the child connections goes down to ZERO.
+ }
+ for (Listener l : mListeners) {
+ l.onParentConnectionChanged(this, mParentConnection);
+ }
+ }
+ }
+
+ public Connection getParentConnection() {
+ return mParentConnection;
+ }
+
+ public List<Connection> getChildConnections() {
+ return mChildConnections;
+ }
+
+ /**
* Sets the value of the {@link #getHandle()} property and notifies listeners.
*
* @param handle The new handle.
@@ -359,6 +426,32 @@
}
/**
+ * TODO(santoscordon): Needs documentation.
+ */
+ protected void setIsConferenceCapable(boolean isConferenceCapable) {
+ if (mIsConferenceCapable != isConferenceCapable) {
+ mIsConferenceCapable = isConferenceCapable;
+ for (Listener l : mListeners) {
+ l.onConferenceCapableChanged(this, mIsConferenceCapable);
+ }
+ }
+ }
+
+ /**
+ * TODO(santoscordon): Needs documentation.
+ */
+ protected void setDestroyed() {
+ // It is possible that onDestroy() will trigger the listener to remove itself which will
+ // result in a concurrent modification exception. To counteract this we make a copy of the
+ // listeners and iterate on that.
+ for (Listener l : new ArrayList<>(mListeners)) {
+ if (mListeners.contains(l)) {
+ l.onDestroyed(this);
+ }
+ }
+ }
+
+ /**
* Notifies this Connection and listeners that the {@link #getCallAudioState()} property
* has a new value.
*
@@ -418,6 +511,11 @@
protected void onDisconnect() {}
/**
+ * Notifies this Connection of a request to disconnect.
+ */
+ protected void onSeparate() {}
+
+ /**
* Notifies this Connection of a request to abort.
*/
protected void onAbort() {}
@@ -444,6 +542,33 @@
*/
protected void onReject() {}
+ /**
+ * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
+ */
+ protected void onPostDialContinue(boolean proceed) {}
+
+ /**
+ * TODO(santoscordon): Needs documentation.
+ */
+ protected void onConference() {}
+
+ /**
+ * TODO(santoscordon): Needs documentation.
+ */
+ protected void onChildrenChanged(List<Connection> children) {}
+
+ private void addChild(Connection connection) {
+ Log.d(this, "adding child %s", connection);
+ mChildConnections.add(connection);
+ onChildrenChanged(mChildConnections);
+ }
+
+ private void removeChild(Connection connection) {
+ Log.d(this, "removing child %s", connection);
+ mChildConnections.remove(connection);
+ onChildrenChanged(mChildConnections);
+ }
+
private void setState(int state) {
Log.d(this, "setState: %s", stateToString(state));
onSetState(state);
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.aidl b/telecomm/java/android/telecomm/ConnectionRequest.aidl
new file mode 100644
index 0000000..72e5c8c
--- /dev/null
+++ b/telecomm/java/android/telecomm/ConnectionRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecomm;
+
+parcelable ConnectionRequest;
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
index c1f1871..bf5727b 100644
--- a/telecomm/java/android/telecomm/ConnectionRequest.java
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -18,23 +18,37 @@
import android.os.Bundle;
import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
/**
* Simple data container encapsulating a request to some entity to
* create a new {@link Connection}.
*/
-public final class ConnectionRequest {
+public final class ConnectionRequest implements Parcelable {
// TODO: Token to limit recursive invocations
// TODO: Consider upgrading "mHandle" to ordered list of handles, indicating a set of phone
// numbers that would satisfy the client's needs, in order of preference
+ private final String mCallId;
private final Uri mHandle;
private final Bundle mExtras;
public ConnectionRequest(Uri handle, Bundle extras) {
- mHandle = handle; mExtras = extras;
+ this(null, handle, extras);
}
+ public ConnectionRequest(String callId, Uri handle, Bundle extras) {
+ mCallId = callId;
+ mHandle = handle;
+ mExtras = extras;
+ }
+
+ /**
+ * An identifier for this call.
+ */
+ public String getCallId() { return mCallId; }
+
/**
* The handle (e.g., phone number) to which the {@link Connection} is to connect.
*/
@@ -54,4 +68,40 @@
: ConnectionService.toLogSafePhoneNumber(mHandle.toString()),
mExtras == null ? "" : mExtras);
}
-}
+
+ /**
+ * Responsible for creating CallInfo objects for deserialized Parcels.
+ */
+ public static final Parcelable.Creator<ConnectionRequest> CREATOR =
+ new Parcelable.Creator<ConnectionRequest> () {
+ @Override
+ public ConnectionRequest createFromParcel(Parcel source) {
+ String callId = source.readString();
+ Uri handle = (Uri) source.readParcelable(getClass().getClassLoader());
+ Bundle extras = (Bundle) source.readParcelable(getClass().getClassLoader());
+ return new ConnectionRequest(callId, handle, extras);
+ }
+
+ @Override
+ public ConnectionRequest[] newArray(int size) {
+ return new ConnectionRequest[size];
+ }
+ };
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes CallInfo object into a serializeable Parcel.
+ */
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeString(mCallId);
+ destination.writeParcelable(mHandle, 0);
+ destination.writeParcelable(mExtras, 0);
+ }}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index aeb1c33..9dfad2d 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -18,11 +18,17 @@
import android.net.Uri;
import android.os.Bundle;
+import android.telephony.DisconnectCause;
+import android.os.SystemClock;
+
+import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* A {@link android.app.Service} that provides telephone connections to
@@ -31,7 +37,6 @@
public abstract class ConnectionService extends CallService {
// Flag controlling whether PII is emitted into the logs
private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
-
private static final Connection NULL_CONNECTION = new Connection() {};
// Mappings from Connections to IDs as understood by the current CallService implementation
@@ -98,6 +103,20 @@
Log.d(this, "Adapter onRingback %b", ringback);
getAdapter().setRequestingRingback(id, ringback);
}
+
+ @Override
+ public void onConferenceCapableChanged(Connection c, boolean isConferenceCapable) {
+ String id = mIdByConnection.get(c);
+ getAdapter().setCanConference(id, isConferenceCapable);
+ }
+
+ /** ${inheritDoc} */
+ @Override
+ public void onParentConnectionChanged(Connection c, Connection parent) {
+ String id = mIdByConnection.get(c);
+ String parentId = parent == null ? null : mIdByConnection.get(parent);
+ getAdapter().setIsConferenced(id, parentId);
+ }
};
@Override
@@ -109,15 +128,13 @@
@Override
public void onResult(Uri handle, Subscription... result) {
boolean isCompatible = result.length > 0;
- Log.d(this, "adapter setIsCompatibleWith "
- + callInfo.getId() + " " + isCompatible);
+ Log.d(this, "adapter setIsCompatibleWith ");
getAdapter().setIsCompatibleWith(callInfo.getId(), isCompatible);
}
@Override
- public void onError(Uri handle, String reason) {
- Log.w(this, "Error in onFindSubscriptions " + callInfo.getHandle()
- + " error: " + reason);
+ public void onError(Uri handle, int code, String msg) {
+ Log.w(this, "Error in onFindSubscriptions %s %d %s", handle, code, msg);
getAdapter().setIsCompatibleWith(callInfo.getId(), false);
}
}
@@ -129,29 +146,32 @@
Log.d(this, "call %s", callInfo);
onCreateConnections(
new ConnectionRequest(
+ callInfo.getId(),
callInfo.getHandle(),
callInfo.getExtras()),
new Response<ConnectionRequest, Connection>() {
@Override
public void onResult(ConnectionRequest request, Connection... result) {
- if (result.length != 1) {
+ if (result != null && result.length != 1) {
Log.d(this, "adapter handleFailedOutgoingCall %s", callInfo);
getAdapter().handleFailedOutgoingCall(
- callInfo.getId(),
+ request,
+ DisconnectCause.ERROR_UNSPECIFIED,
"Created " + result.length + " Connections, expected 1");
for (Connection c : result) {
c.abort();
}
} else {
- addConnection(callInfo.getId(), result[0]);
- Log.d(this, "adapter handleSuccessfulOutgoingCall %s", callInfo.getId());
+ Log.d(this, "adapter handleSuccessfulOutgoingCall %s",
+ callInfo.getId());
getAdapter().handleSuccessfulOutgoingCall(callInfo.getId());
+ addConnection(callInfo.getId(), result[0]);
}
}
@Override
- public void onError(ConnectionRequest request, String reason) {
- getAdapter().handleFailedOutgoingCall(callInfo.getId(), reason);
+ public void onError(ConnectionRequest request, int code, String msg) {
+ getAdapter().handleFailedOutgoingCall(request, code, msg);
}
}
);
@@ -168,15 +188,17 @@
Log.d(this, "setIncomingCallId %s %s", callId, extras);
onCreateIncomingConnection(
new ConnectionRequest(
+ callId,
null, // TODO: Can we obtain this from "extras"?
extras),
new Response<ConnectionRequest, Connection>() {
@Override
public void onResult(ConnectionRequest request, Connection... result) {
- if (result.length != 1) {
+ if (result != null && result.length != 1) {
Log.d(this, "adapter handleFailedOutgoingCall %s", callId);
getAdapter().handleFailedOutgoingCall(
- callId,
+ request,
+ DisconnectCause.ERROR_UNSPECIFIED,
"Created " + result.length + " Connections, expected 1");
for (Connection c : result) {
c.abort();
@@ -195,8 +217,9 @@
}
@Override
- public void onError(ConnectionRequest request, String reason) {
- Log.d(this, "adapter failed setIncomingCallId %s %s", request, reason);
+ public void onError(ConnectionRequest request, int code, String msg) {
+ Log.d(this, "adapter failed setIncomingCallId %s %d %s",
+ request, code, msg);
}
}
);
@@ -252,27 +275,43 @@
/** @hide */
@Override
- public final void addToConference(String conferenceCallId, List<String> callIds) {
- Log.d(this, "addToConference %s, %s", conferenceCallId, callIds);
+ public final void conference(final String conferenceCallId, String callId) {
+ Log.d(this, "conference %s, %s", conferenceCallId, callId);
- List<Connection> connections = new LinkedList<>();
- for (String id : callIds) {
- Connection connection = findConnectionForAction(id, "addToConference");
- if (connection == NULL_CONNECTION) {
- Log.w(this, "Connection missing in conference request %s.", id);
- return;
- }
- connections.add(connection);
+ Connection connection = findConnectionForAction(callId, "conference");
+ if (connection == NULL_CONNECTION) {
+ Log.w(this, "Connection missing in conference request %s.", callId);
+ return;
}
- // TODO(santoscordon): Find an existing conference call or create a new one. Then call
- // conferenceWith on it.
+ onCreateConferenceConnection(conferenceCallId, connection,
+ new Response<String, Connection>() {
+ /** ${inheritDoc} */
+ @Override
+ public void onResult(String ignored, Connection... result) {
+ Log.d(this, "onCreateConference.Response %s", (Object[]) result);
+ if (result != null && result.length == 1) {
+ Connection conferenceConnection = result[0];
+ if (!mIdByConnection.containsKey(conferenceConnection)) {
+ Log.v(this, "sending new conference call %s", conferenceCallId);
+ getAdapter().addConferenceCall(conferenceCallId);
+ addConnection(conferenceCallId, conferenceConnection);
+ }
+ }
+ }
+
+ /** ${inheritDoc} */
+ @Override
+ public void onError(String request, int code, String reason) {
+ // no-op
+ }
+ });
}
/** @hide */
@Override
- public final void splitFromConference(String conferenceCallId, String callId) {
- Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId);
+ public final void splitFromConference(String callId) {
+ Log.d(this, "splitFromConference(%s)", callId);
Connection connection = findConnectionForAction(callId, "splitFromConference");
if (connection == NULL_CONNECTION) {
@@ -283,6 +322,32 @@
// TODO(santoscordon): Find existing conference call and invoke split(connection).
}
+ @Override
+ public final void onPostDialContinue(String callId, boolean proceed) {
+ Log.d(this, "onPostDialContinue(%s)", callId);
+
+ Connection connection = findConnectionForAction(callId, "onPostDialContinue");
+ if (connection == NULL_CONNECTION) {
+ Log.w(this, "Connection missing in post-dial request %s.", callId);
+ return;
+ }
+ connection.onPostDialContinue(proceed);
+ }
+
+ @Override
+ public final void onPostDialWait(Connection conn, String remaining) {
+ Log.d(this, "onPostDialWait(%s, %s)", conn, remaining);
+
+ getAdapter().onPostDialWait(mIdByConnection.get(conn), remaining);
+ }
+
+ /**
+ * Returns all connections currently associated with this connection service.
+ */
+ public Collection<Connection> getAllConnections() {
+ return mConnectionById.values();
+ }
+
/**
* Find a set of Subscriptions matching a given handle (e.g. phone number).
*
@@ -304,6 +369,21 @@
Response<ConnectionRequest, Connection> callback) {}
/**
+ * Returns a new or existing conference connection when the the user elects to convert the
+ * specified connection into a conference call. The specified connection can be any connection
+ * which had previously specified itself as conference-capable including both simple connections
+ * and connections previously returned from this method.
+ *
+ * @param connection The connection from which the user opted to start a conference call.
+ * @param token The token to be passed into the response callback.
+ * @param callback The callback for providing the potentially-new conference connection.
+ */
+ public void onCreateConferenceConnection(
+ String token,
+ Connection connection,
+ Response<String, Connection> callback) {}
+
+ /**
* Create a Connection to match an incoming connection notification.
*
* @param request Data encapsulating details of the desired Connection.
@@ -313,6 +393,20 @@
ConnectionRequest request,
Response<ConnectionRequest, Connection> callback) {}
+ /**
+ * Notifies that a connection has been added to this connection service and sent to Telecomm.
+ *
+ * @param connection The connection which was added.
+ */
+ public void onConnectionAdded(Connection connection) {}
+
+ /**
+ * Notified that a connection has been removed from this connection service.
+ *
+ * @param connection The connection which was removed.
+ */
+ public void onConnectionRemoved(Connection connection) {}
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
@@ -362,12 +456,14 @@
mConnectionById.put(callId, connection);
mIdByConnection.put(connection, callId);
connection.addConnectionListener(mConnectionListener);
+ onConnectionAdded(connection);
}
private void removeConnection(Connection connection) {
connection.removeConnectionListener(mConnectionListener);
mConnectionById.remove(mIdByConnection.get(connection));
mIdByConnection.remove(connection);
+ onConnectionRemoved(connection);
}
private Connection findConnectionForAction(String callId, String action) {
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index 6838ede..86b7a50 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -174,13 +174,14 @@
* will pause playing the tones and notify the {@link InCallService} that the call is in the
* {@link InCallService#setPostDialWait(String,String)} state. When the user decides to continue
* the postdial sequence, the {@link InCallService} should invoke the
- * {@link #postDialContinue(String)} method.
+ * {@link #postDialContinue(String,boolean)} method.
*
* @param callId The unique ID of the call for which postdial string playing should continue.
+ * @param proceed Whether or not to continue with the post-dial sequence.
*/
- public void postDialContinue(String callId) {
+ public void postDialContinue(String callId, boolean proceed) {
try {
- mAdapter.postDialContinue(callId);
+ mAdapter.postDialContinue(callId, proceed);
} catch (RemoteException e) {
}
}
@@ -198,15 +199,14 @@
}
/**
- * Instructs Telecomm to conference the specified calls together.
+ * Instructs Telecomm to conference the specified call.
*
* @param callId The unique ID of the call.
- * @param callIdToConference The unique ID of the call to conference with.
* @hide
*/
- void conferenceWith(String callId, String callIdToConference) {
+ public void conference(String callId) {
try {
- mAdapter.conferenceWith(callId, callIdToConference);
+ mAdapter.conference(callId);
} catch (RemoteException ignored) {
}
}
@@ -218,7 +218,7 @@
* @param callId The unique ID of the call.
* @hide
*/
- void splitFromConference(String callId) {
+ public void splitFromConference(String callId) {
try {
mAdapter.splitFromConference(callId);
} catch (RemoteException ignored) {
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index 346d207..432e378 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -31,14 +31,14 @@
public final class InCallCall implements Parcelable {
private final String mId;
private final CallState mState;
- private final int mDisconnectCause;
+ private final int mDisconnectCauseCode;
+ private final String mDisconnectCauseMsg;
private final int mCapabilities;
private final long mConnectTimeMillis;
private final Uri mHandle;
private final GatewayInfo mGatewayInfo;
private final CallServiceDescriptor mCurrentCallServiceDescriptor;
private final CallServiceDescriptor mHandoffCallServiceDescriptor;
- private final List<String> mConferenceCapableCallIds;
private final String mParentCallId;
private final List<String> mChildCallIds;
@@ -47,42 +47,42 @@
public InCallCall(
String id,
CallState state,
- int disconnectCause,
+ int disconnectCauseCode,
+ String disconnectCauseMsg,
int capabilities,
long connectTimeMillis,
Uri handle,
GatewayInfo gatewayInfo,
CallServiceDescriptor descriptor,
CallServiceDescriptor handoffDescriptor) {
- this(id, state, disconnectCause, capabilities, connectTimeMillis, handle, gatewayInfo,
- descriptor, handoffDescriptor, Collections.EMPTY_LIST, null,
- Collections.EMPTY_LIST);
+ this(id, state, disconnectCauseCode, disconnectCauseMsg, capabilities, connectTimeMillis,
+ handle, gatewayInfo, descriptor, handoffDescriptor, null, Collections.EMPTY_LIST);
}
/** @hide */
public InCallCall(
String id,
CallState state,
- int disconnectCause,
+ int disconnectCauseCode,
+ String disconnectCauseMsg,
int capabilities,
long connectTimeMillis,
Uri handle,
GatewayInfo gatewayInfo,
CallServiceDescriptor descriptor,
CallServiceDescriptor handoffDescriptor,
- List<String> conferenceCapableCallIds,
String parentCallId,
List<String> childCallIds) {
mId = id;
mState = state;
- mDisconnectCause = disconnectCause;
+ mDisconnectCauseCode = disconnectCauseCode;
+ mDisconnectCauseMsg = disconnectCauseMsg;
mCapabilities = capabilities;
mConnectTimeMillis = connectTimeMillis;
mHandle = handle;
mGatewayInfo = gatewayInfo;
mCurrentCallServiceDescriptor = descriptor;
mHandoffCallServiceDescriptor = handoffDescriptor;
- mConferenceCapableCallIds = conferenceCapableCallIds;
mParentCallId = parentCallId;
mChildCallIds = childCallIds;
}
@@ -101,8 +101,16 @@
* Reason for disconnection, values are defined in {@link DisconnectCause}. Valid when call
* state is {@link CallState#DISCONNECTED}.
*/
- public int getDisconnectCause() {
- return mDisconnectCause;
+ public int getDisconnectCauseCode() {
+ return mDisconnectCauseCode;
+ }
+
+ /**
+ * Further optional textual information about the reason for disconnection. Valid when call
+ * state is {@link CallState#DISCONNECTED}.
+ */
+ public String getDisconnectCauseMsg() {
+ return mDisconnectCauseMsg;
}
// Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
@@ -139,14 +147,6 @@
}
/**
- * The calls with which this call can conference.
- * @hide
- */
- public List<String> getConferenceCapableCallIds() {
- return mConferenceCapableCallIds;
- }
-
- /**
* The conference call to which this call is conferenced. Null if not conferenced.
* @hide
*/
@@ -170,7 +170,8 @@
public InCallCall createFromParcel(Parcel source) {
String id = source.readString();
CallState state = CallState.valueOf(source.readString());
- int disconnectCause = source.readInt();
+ int disconnectCauseCode = source.readInt();
+ String disconnectCauseMsg = source.readString();
int capabilities = source.readInt();
long connectTimeMillis = source.readLong();
ClassLoader classLoader = InCallCall.class.getClassLoader();
@@ -178,13 +179,11 @@
GatewayInfo gatewayInfo = source.readParcelable(classLoader);
CallServiceDescriptor descriptor = source.readParcelable(classLoader);
CallServiceDescriptor handoffDescriptor = source.readParcelable(classLoader);
- List<String> conferenceCapableCallIds = new ArrayList<>();
- source.readList(conferenceCapableCallIds, classLoader);
String parentCallId = source.readString();
List<String> childCallIds = new ArrayList<>();
source.readList(childCallIds, classLoader);
- return new InCallCall(id, state, disconnectCause, capabilities, connectTimeMillis,
- handle, gatewayInfo, descriptor, handoffDescriptor, conferenceCapableCallIds,
+ return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg, capabilities,
+ connectTimeMillis, handle, gatewayInfo, descriptor, handoffDescriptor,
parentCallId, childCallIds);
}
@@ -205,15 +204,20 @@
public void writeToParcel(Parcel destination, int flags) {
destination.writeString(mId);
destination.writeString(mState.name());
- destination.writeInt(mDisconnectCause);
+ destination.writeInt(mDisconnectCauseCode);
+ destination.writeString(mDisconnectCauseMsg);
destination.writeInt(mCapabilities);
destination.writeLong(mConnectTimeMillis);
destination.writeParcelable(mHandle, 0);
destination.writeParcelable(mGatewayInfo, 0);
destination.writeParcelable(mCurrentCallServiceDescriptor, 0);
destination.writeParcelable(mHandoffCallServiceDescriptor, 0);
- destination.writeList(mConferenceCapableCallIds);
destination.writeString(mParentCallId);
destination.writeList(mChildCallIds);
}
+
+ @Override
+ public String toString() {
+ return String.format("[%s, parent:%s, children:%s]", mId, mParentCallId, mChildCallIds);
+ }
}
diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java
index 3a63077..c7dd23a 100644
--- a/telecomm/java/android/telecomm/InCallService.java
+++ b/telecomm/java/android/telecomm/InCallService.java
@@ -203,7 +203,7 @@
* {@link #setPostDial(String,String)} state but is now waiting for user confirmation before the
* remaining digits can be sent. Normal transitions are to {@link #setPostDial(String,String)}
* when the user asks Telecomm to proceed with the post-dial sequence and the in-call app
- * informs Telecomm of this by invoking {@link InCallAdapter#postDialContinue(String)}.
+ * informs Telecomm of this by invoking {@link InCallAdapter#postDialContinue(String,boolean)}.
*
* @param callId The identifier of the call changing state.
* @param remaining The remaining postdial string to be dialed.
diff --git a/telecomm/java/android/telecomm/Response.java b/telecomm/java/android/telecomm/Response.java
index 14f8340..13c0702 100644
--- a/telecomm/java/android/telecomm/Response.java
+++ b/telecomm/java/android/telecomm/Response.java
@@ -33,7 +33,8 @@
* Indicates the inability to provide results.
*
* @param request The original request.
- * @param reason The reason for the failure.
+ * @param code An integer code indicating the reason for failure.
+ * @param msg A message explaining the reason for failure.
*/
- void onError(IN request, String reason);
+ void onError(IN request, int code, String msg);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
index 771a3ae..827f331 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
@@ -56,7 +56,9 @@
void stopDtmfTone(String callId);
- void addToConference(String conferenceCallId, in List<String> callIds);
+ void conference(String conferenceCallId, String callId);
- void splitFromConference(String conferenceCallId, String callId);
+ void splitFromConference(String callId);
+
+ void onPostDialContinue(String callId, boolean proceed);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index a92b176..6e176eb 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -17,6 +17,7 @@
package com.android.internal.telecomm;
import android.telecomm.CallInfo;
+import android.telecomm.ConnectionRequest;
/**
* Internal remote callback interface for call services.
@@ -32,7 +33,7 @@
void handleSuccessfulOutgoingCall(String callId);
- void handleFailedOutgoingCall(String callId, String errorMessage);
+ void handleFailedOutgoingCall(in ConnectionRequest request, int errorCode, String errorMessage);
void setActive(String callId);
@@ -46,9 +47,15 @@
void setRequestingRingback(String callId, boolean ringing);
- void setCanConferenceWith(String callId, in List<String> conferenceCapableCallIds);
+ void setCanConference(String callId, boolean canConference);
- void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced);
+ void setIsConferenced(String callId, String conferenceCallId);
+
+ void addConferenceCall(String callId, in CallInfo callInfo);
void removeCall(String callId);
+
+ void onPostDialWait(String callId, String remaining);
+
+ void handoffCall(String callId);
}
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
index 6a27217..5717456 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
@@ -44,11 +44,11 @@
void stopDtmfTone(String callId);
- void postDialContinue(String callId);
+ void postDialContinue(String callId, boolean proceed);
void handoffCall(String callId);
- void conferenceWith(String callId, String callIdToConference);
+ void conference(String callId);
void splitFromConference(String callId);
}
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 8681344..d2044be 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -97,11 +97,62 @@
public static final int CDMA_ACCESS_BLOCKED = 35;
/** Unknown error or not specified */
public static final int ERROR_UNSPECIFIED = 36;
+ /**
+ * Only emergency numbers are allowed, but we tried to dial
+ * a non-emergency number.
+ */
+ // TODO: This should be the same as NOT_EMERGENCY
+ public static final int EMERGENCY_ONLY = 37;
+ /**
+ * The supplied CALL Intent didn't contain a valid phone number.
+ */
+ public static final int NO_PHONE_NUMBER_SUPPLIED = 38;
+ /**
+ * Our initial phone number was actually an MMI sequence.
+ */
+ public static final int DIALED_MMI = 39;
+ /**
+ * We tried to call a voicemail: URI but the device has no
+ * voicemail number configured.
+ */
+ public static final int VOICEMAIL_NUMBER_MISSING = 40;
+ /**
+ * This status indicates that InCallScreen should display the
+ * CDMA-specific "call lost" dialog. (If an outgoing call fails,
+ * and the CDMA "auto-retry" feature is enabled, *and* the retried
+ * call fails too, we display this specific dialog.)
+ *
+ * TODO: this is currently unused, since the "call lost" dialog
+ * needs to be triggered by a *disconnect* event, rather than when
+ * the InCallScreen first comes to the foreground. For now we use
+ * the needToShowCallLostDialog field for this (see below.)
+ */
+ public static final int CDMA_CALL_LOST = 41;
+ /**
+ * This status indicates that the call was placed successfully,
+ * but additionally, the InCallScreen needs to display the
+ * "Exiting ECM" dialog.
+ *
+ * (Details: "Emergency callback mode" is a CDMA-specific concept
+ * where the phone disallows data connections over the cell
+ * network for some period of time after you make an emergency
+ * call. If the phone is in ECM and you dial a non-emergency
+ * number, that automatically *cancels* ECM, but we additionally
+ * need to warn the user that ECM has been canceled (see bug
+ * 4207607.))
+ *
+ * TODO: Rethink where the best place to put this is. It is not a notification
+ * of a failure of the connection -- it is an additional message that accompanies
+ * a successful connection giving the user important information about what happened.
+ *
+ * {@hide}
+ */
+ public static final int EXITED_ECM = 42;
/** Smallest valid value for call disconnect codes. */
public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED;
/** Largest valid value for call disconnect codes. */
- public static final int MAXIMUM_VALID_VALUE = ERROR_UNSPECIFIED;
+ public static final int MAXIMUM_VALID_VALUE = EXITED_ECM;
/** Private constructor to avoid class instantiation. */
private DisconnectCause() {
@@ -181,6 +232,18 @@
return "CDMA_NOT_EMERGENCY";
case CDMA_ACCESS_BLOCKED:
return "CDMA_ACCESS_BLOCKED";
+ case EMERGENCY_ONLY:
+ return "EMERGENCY_ONLY";
+ case NO_PHONE_NUMBER_SUPPLIED:
+ return "NO_PHONE_NUMBER_SUPPLIED";
+ case DIALED_MMI:
+ return "DIALED_MMI";
+ case VOICEMAIL_NUMBER_MISSING:
+ return "VOICEMAIL_NUMBER_MISSING";
+ case CDMA_CALL_LOST:
+ return "CDMA_CALL_LOST";
+ case EXITED_ECM:
+ return "EXITED_ECM";
case ERROR_UNSPECIFIED:
return "ERROR_UNSPECIFIED";
default:
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 9da032a..ed7f6b8 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1742,15 +1742,15 @@
/**
* Checks if a given number is an emergency number for the country that the user is in.
- *
- * @param number the number to look up.
* @param context the specific context which the number should be checked against
+ * @param number the number to look up.
+ *
* @return true if the specified number is an emergency number for the country the user
* is currently in.
*/
- public static boolean isLocalEmergencyNumber(String number, Context context) {
- return isLocalEmergencyNumberInternal(number,
- context,
+ public static boolean isLocalEmergencyNumber(Context context, String number) {
+ return isLocalEmergencyNumberInternal(context,
+ number,
true /* useExactMatch */);
}
@@ -1767,27 +1767,26 @@
* This method is intended for internal use by the phone app when
* deciding whether to allow ACTION_CALL intents from 3rd party apps
* (where we're required to *not* allow emergency calls to be placed.)
- *
- * @param number the number to look up.
* @param context the specific context which the number should be checked against
+ * @param number the number to look up.
+ *
* @return true if the specified number is an emergency number for a local country, based on the
* CountryDetector.
*
* @see android.location.CountryDetector
* @hide
*/
- public static boolean isPotentialLocalEmergencyNumber(String number, Context context) {
- return isLocalEmergencyNumberInternal(number,
- context,
+ public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
+ return isLocalEmergencyNumberInternal(context,
+ number,
false /* useExactMatch */);
}
/**
* Helper function for isLocalEmergencyNumber() and
* isPotentialLocalEmergencyNumber().
- *
- * @param number the number to look up.
* @param context the specific context which the number should be checked against
+ * @param number the number to look up.
* @param useExactMatch if true, consider a number to be an emergency
* number only if it *exactly* matches a number listed in
* the RIL / SIM. If false, a number is considered to be an
@@ -1799,8 +1798,8 @@
*
* @see android.location.CountryDetector
*/
- private static boolean isLocalEmergencyNumberInternal(String number,
- Context context,
+ private static boolean isLocalEmergencyNumberInternal(Context context,
+ String number,
boolean useExactMatch) {
String countryIso;
CountryDetector detector = (CountryDetector) context.getSystemService(
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ffa9a4e..3f65bca 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -16,7 +16,7 @@
package android.telephony;
-import android.annotation.PrivateApi;
+import android.annotation.SystemApi;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.ComponentName;
@@ -1961,11 +1961,11 @@
}
/**
- * Expose the rest of ITelephony to @PrivateApi
+ * Expose the rest of ITelephony to @SystemApi
*/
/** @hide */
- @PrivateApi
+ @SystemApi
public void dial(String number) {
try {
getITelephony().dial(number);
@@ -1975,7 +1975,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public void call(String callingPackage, String number) {
try {
getITelephony().call(callingPackage, number);
@@ -1985,7 +1985,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean showCallScreen() {
try {
getTelecommService().showCallScreen(false);
@@ -1997,7 +1997,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean showCallScreenWithDialpad(boolean showDialpad) {
try {
getTelecommService().showCallScreen(showDialpad);
@@ -2009,7 +2009,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean endCall() {
try {
return getITelephony().endCall();
@@ -2020,7 +2020,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public void answerRingingCall() {
try {
getITelephony().answerRingingCall();
@@ -2030,7 +2030,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public void silenceRinger() {
try {
getTelecommService().silenceRinger();
@@ -2040,7 +2040,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean isOffhook() {
try {
return getITelephony().isOffhook();
@@ -2051,7 +2051,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean isRinging() {
try {
return getITelephony().isRinging();
@@ -2062,7 +2062,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean isIdle() {
try {
return getITelephony().isIdle();
@@ -2073,7 +2073,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean isRadioOn() {
try {
return getITelephony().isRadioOn();
@@ -2084,7 +2084,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean isSimPinEnabled() {
try {
return getITelephony().isSimPinEnabled();
@@ -2095,7 +2095,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public void cancelMissedCallsNotification() {
try {
getITelephony().cancelMissedCallsNotification();
@@ -2105,7 +2105,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean supplyPin(String pin) {
try {
return getITelephony().supplyPin(pin);
@@ -2116,7 +2116,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean supplyPuk(String puk, String pin) {
try {
return getITelephony().supplyPuk(puk, pin);
@@ -2127,7 +2127,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public int[] supplyPinReportResult(String pin) {
try {
return getITelephony().supplyPinReportResult(pin);
@@ -2138,7 +2138,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public int[] supplyPukReportResult(String puk, String pin) {
try {
return getITelephony().supplyPukReportResult(puk, pin);
@@ -2149,7 +2149,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean handlePinMmi(String dialString) {
try {
return getITelephony().handlePinMmi(dialString);
@@ -2160,7 +2160,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public void toggleRadioOnOff() {
try {
getITelephony().toggleRadioOnOff();
@@ -2170,7 +2170,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean setRadio(boolean turnOn) {
try {
return getITelephony().setRadio(turnOn);
@@ -2181,7 +2181,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean setRadioPower(boolean turnOn) {
try {
return getITelephony().setRadioPower(turnOn);
@@ -2192,7 +2192,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public void updateServiceLocation() {
try {
getITelephony().updateServiceLocation();
@@ -2202,7 +2202,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public int enableApnType(String type) {
try {
return getITelephony().enableApnType(type);
@@ -2213,7 +2213,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public int disableApnType(String type) {
try {
return getITelephony().disableApnType(type);
@@ -2224,7 +2224,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean enableDataConnectivity() {
try {
return getITelephony().enableDataConnectivity();
@@ -2235,7 +2235,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean disableDataConnectivity() {
try {
return getITelephony().disableDataConnectivity();
@@ -2246,7 +2246,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean isDataConnectivityPossible() {
try {
return getITelephony().isDataConnectivityPossible();
@@ -2257,7 +2257,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean needsOtaServiceProvisioning() {
try {
return getITelephony().needsOtaServiceProvisioning();
@@ -2268,7 +2268,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public void setDataEnabled(boolean enable) {
try {
getITelephony().setDataEnabled(enable);
@@ -2278,7 +2278,7 @@
}
/** @hide */
- @PrivateApi
+ @SystemApi
public boolean getDataEnabled() {
try {
return getITelephony().getDataEnabled();
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index f6143ed..f8dd7cf 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -276,7 +276,7 @@
// Change the callerInfo number ONLY if it is an emergency number
// or if it is the voicemail number. If it is either, take a
// shortcut and skip the query.
- if (PhoneNumberUtils.isLocalEmergencyNumber(number, context)) {
+ if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
return new CallerInfo().markAsEmergency(context);
} else if (PhoneNumberUtils.isVoiceMailNumber(number)) {
return new CallerInfo().markAsVoiceMail();
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index 74f73b5..34fed5e 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -399,7 +399,7 @@
cw.number = number;
// check to see if these are recognized numbers, and use shortcuts if we can.
- if (PhoneNumberUtils.isLocalEmergencyNumber(number, context)) {
+ if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
cw.event = EVENT_EMERGENCY_NUMBER;
} else if (PhoneNumberUtils.isVoiceMailNumber(number)) {
cw.event = EVENT_VOICEMAIL_NUMBER;
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 3e8db06..6fc64ae 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -119,6 +119,19 @@
in PendingIntent sentIntent, in PendingIntent deliveryIntent);
/**
+ * Inject an SMS PDU into the android platform.
+ *
+ * @param pdu is the byte array of pdu to be injected into android application framework
+ * @param format is the format of SMS pdu (android.telephony.SmsMessage.FORMAT_3GPP or
+ * android.telephony.SmsMessage.FORMAT_3GPP2)
+ * @param receivedIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is successfully received by the
+ * android application framework. This intent is broadcasted at
+ * the same time an SMS received from radio is acknowledged back.
+ */
+ void injectSmsPdu(in byte[] pdu, String format, in PendingIntent receivedIntent);
+
+ /**
* Send a multi-part text based SMS.
*
* @param destinationAddress the address to send the message to
diff --git a/tests/Camera2Tests/CameraToo/Android.mk b/tests/Camera2Tests/CameraToo/Android.mk
new file mode 100644
index 0000000..7e5911d
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/Android.mk
@@ -0,0 +1,23 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := CameraToo
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+include $(BUILD_PACKAGE)
diff --git a/tests/Camera2Tests/CameraToo/AndroidManifest.xml b/tests/Camera2Tests/CameraToo/AndroidManifest.xml
new file mode 100644
index 0000000..a92b5d8
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.camera2.cameratoo">
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application android:label="CameraToo">
+ <activity
+ android:name=".CameraTooActivity"
+ android:screenOrientation="portrait">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml b/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml
new file mode 100644
index 0000000..f93f177
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/mainSurfaceView"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:onClick="onClickOnSurfaceView" />
diff --git a/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java b/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java
new file mode 100644
index 0000000..c630bad
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.camera2.cameratoo;
+
+import android.app.Activity;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Size;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * A basic demonstration of how to write a point-and-shoot camera app against the new
+ * android.hardware.camera2 API.
+ */
+public class CameraTooActivity extends Activity {
+ /** Output files will be saved as /sdcard/Pictures/cameratoo*.jpg */
+ static final String CAPTURE_FILENAME_PREFIX = "cameratoo";
+ /** Tag to distinguish log prints. */
+ static final String TAG = "CameraToo";
+
+ /** An additional thread for running tasks that shouldn't block the UI. */
+ HandlerThread mBackgroundThread;
+ /** Handler for running tasks in the background. */
+ Handler mBackgroundHandler;
+ /** Handler for running tasks on the UI thread. */
+ Handler mForegroundHandler;
+ /** View for displaying the camera preview. */
+ SurfaceView mSurfaceView;
+ /** Used to retrieve the captured image when the user takes a snapshot. */
+ ImageReader mCaptureBuffer;
+ /** Handle to the Android camera services. */
+ CameraManager mCameraManager;
+ /** The specific camera device that we're using. */
+ CameraDevice mCamera;
+ /** Our image capture session. */
+ CameraCaptureSession mCaptureSession;
+
+ /**
+ * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose
+ * width and height are at least as large as the respective requested values.
+ * @param choices The list of sizes that the camera supports for the intended output class
+ * @param width The minimum desired width
+ * @param height The minimum desired height
+ * @return The optimal {@code Size}, or an arbitrary one if none were big enough
+ */
+ static Size chooseBigEnoughSize(Size[] choices, int width, int height) {
+ // Collect the supported resolutions that are at least as big as the preview Surface
+ List<Size> bigEnough = new ArrayList<Size>();
+ for (Size option : choices) {
+ if (option.getWidth() >= width && option.getHeight() >= height) {
+ bigEnough.add(option);
+ }
+ }
+
+ // Pick the smallest of those, assuming we found any
+ if (bigEnough.size() > 0) {
+ return Collections.min(bigEnough, new CompareSizesByArea());
+ } else {
+ Log.e(TAG, "Couldn't find any suitable preview size");
+ return choices[0];
+ }
+ }
+
+ /**
+ * Compares two {@code Size}s based on their areas.
+ */
+ static class CompareSizesByArea implements Comparator<Size> {
+ @Override
+ public int compare(Size lhs, Size rhs) {
+ // We cast here to ensure the multiplications won't overflow
+ return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
+ (long) rhs.getWidth() * rhs.getHeight());
+ }
+ }
+
+ /**
+ * Called when our {@code Activity} gains focus. <p>Starts initializing the camera.</p>
+ */
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // Start a background thread to manage camera requests
+ mBackgroundThread = new HandlerThread("background");
+ mBackgroundThread.start();
+ mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
+ mForegroundHandler = new Handler(getMainLooper());
+
+ mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
+
+ // Inflate the SurfaceView, set it as the main layout, and attach a listener
+ View layout = getLayoutInflater().inflate(R.layout.mainactivity, null);
+ mSurfaceView = (SurfaceView) layout.findViewById(R.id.mainSurfaceView);
+ mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
+ setContentView(mSurfaceView);
+
+ // Control flow continues in mSurfaceHolderCallback.surfaceChanged()
+ }
+
+ /**
+ * Called when our {@code Activity} loses focus. <p>Tears everything back down.</p>
+ */
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ try {
+ // Ensure SurfaceHolderCallback#surfaceChanged() will run again if the user returns
+ mSurfaceView.getHolder().setFixedSize(/*width*/0, /*height*/0);
+
+ // Cancel any stale preview jobs
+ if (mCaptureSession != null) {
+ mCaptureSession.close();
+ mCaptureSession = null;
+ }
+ } finally {
+ if (mCamera != null) {
+ mCamera.close();
+ mCamera = null;
+ }
+ }
+
+ // Finish processing posted messages, then join on the handling thread
+ mBackgroundThread.quitSafely();
+ try {
+ mBackgroundThread.join();
+ } catch (InterruptedException ex) {
+ Log.e(TAG, "Background worker thread was interrupted while joined", ex);
+ }
+
+ // Close the ImageReader now that the background thread has stopped
+ if (mCaptureBuffer != null) mCaptureBuffer.close();
+ }
+
+ /**
+ * Called when the user clicks on our {@code SurfaceView}, which has ID {@code mainSurfaceView}
+ * as defined in the {@code mainactivity.xml} layout file. <p>Captures a full-resolution image
+ * and saves it to permanent storage.</p>
+ */
+ public void onClickOnSurfaceView(View v) {
+ if (mCaptureSession != null) {
+ try {
+ CaptureRequest.Builder requester =
+ mCamera.createCaptureRequest(mCamera.TEMPLATE_STILL_CAPTURE);
+ requester.addTarget(mCaptureBuffer.getSurface());
+ try {
+ // This handler can be null because we aren't actually attaching any callback
+ mCaptureSession.capture(requester.build(), /*listener*/null, /*handler*/null);
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Failed to file actual capture request", ex);
+ }
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Failed to build actual capture request", ex);
+ }
+ } else {
+ Log.e(TAG, "User attempted to perform a capture outside our session");
+ }
+
+ // Control flow continues in mImageCaptureListener.onImageAvailable()
+ }
+
+ /**
+ * Callbacks invoked upon state changes in our {@code SurfaceView}.
+ */
+ final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
+ /** The camera device to use, or null if we haven't yet set a fixed surface size. */
+ private String mCameraId;
+
+ /** Whether we received a change callback after setting our fixed surface size. */
+ private boolean mGotSecondCallback;
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ // This is called every time the surface returns to the foreground
+ Log.i(TAG, "Surface created");
+ mCameraId = null;
+ mGotSecondCallback = false;
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ Log.i(TAG, "Surface destroyed");
+ holder.removeCallback(this);
+ // We don't stop receiving callbacks forever because onResume() will reattach us
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ // On the first invocation, width and height were automatically set to the view's size
+ if (mCameraId == null) {
+ // Find the device's back-facing camera and set the destination buffer sizes
+ try {
+ for (String cameraId : mCameraManager.getCameraIdList()) {
+ CameraCharacteristics cameraCharacteristics =
+ mCameraManager.getCameraCharacteristics(cameraId);
+ if (cameraCharacteristics.get(cameraCharacteristics.LENS_FACING) ==
+ CameraCharacteristics.LENS_FACING_BACK) {
+ Log.i(TAG, "Found a back-facing camera");
+ StreamConfigurationMap info = cameraCharacteristics
+ .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ // Bigger is better when it comes to saving our image
+ Size largestSize = Collections.max(
+ Arrays.asList(info.getOutputSizes(ImageFormat.JPEG)),
+ new CompareSizesByArea());
+
+ // Prepare an ImageReader in case the user wants to capture images
+ Log.i(TAG, "Capture size: " + largestSize);
+ mCaptureBuffer = ImageReader.newInstance(largestSize.getWidth(),
+ largestSize.getHeight(), ImageFormat.JPEG, /*maxImages*/2);
+ mCaptureBuffer.setOnImageAvailableListener(
+ mImageCaptureListener, mBackgroundHandler);
+
+ // Danger, W.R.! Attempting to use too large a preview size could
+ // exceed the camera bus' bandwidth limitation, resulting in
+ // gorgeous previews but the storage of garbage capture data.
+ Log.i(TAG, "SurfaceView size: " +
+ mSurfaceView.getWidth() + 'x' + mSurfaceView.getHeight());
+ Size optimalSize = chooseBigEnoughSize(
+ info.getOutputSizes(SurfaceHolder.class), width, height);
+
+ // Set the SurfaceHolder to use the camera's largest supported size
+ Log.i(TAG, "Preview size: " + optimalSize);
+ SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
+ surfaceHolder.setFixedSize(optimalSize.getWidth(),
+ optimalSize.getHeight());
+
+ mCameraId = cameraId;
+ return;
+
+ // Control flow continues with this method one more time
+ // (since we just changed our own size)
+ }
+ }
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Unable to list cameras", ex);
+ }
+
+ Log.e(TAG, "Didn't find any back-facing cameras");
+ // This is the second time the method is being invoked: our size change is complete
+ } else if (!mGotSecondCallback) {
+ if (mCamera != null) {
+ Log.e(TAG, "Aborting camera open because it hadn't been closed");
+ return;
+ }
+
+ // Open the camera device
+ try {
+ mCameraManager.openCamera(mCameraId, mCameraStateListener,
+ mBackgroundHandler);
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Failed to configure output surface", ex);
+ }
+ mGotSecondCallback = true;
+
+ // Control flow continues in mCameraStateListener.onOpened()
+ }
+ }};
+
+ /**
+ * Calledbacks invoked upon state changes in our {@code CameraDevice}. <p>These are run on
+ * {@code mBackgroundThread}.</p>
+ */
+ final CameraDevice.StateListener mCameraStateListener =
+ new CameraDevice.StateListener() {
+ @Override
+ public void onOpened(CameraDevice camera) {
+ Log.i(TAG, "Successfully opened camera");
+ mCamera = camera;
+ try {
+ List<Surface> outputs = Arrays.asList(
+ mSurfaceView.getHolder().getSurface(), mCaptureBuffer.getSurface());
+ camera.createCaptureSession(outputs, mCaptureSessionListener,
+ mBackgroundHandler);
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Failed to create a capture session", ex);
+ }
+
+ // Control flow continues in mCaptureSessionListener.onConfigured()
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {
+ Log.e(TAG, "Camera was disconnected");
+ }
+
+ @Override
+ public void onError(CameraDevice camera, int error) {
+ Log.e(TAG, "State error on device '" + camera.getId() + "': code " + error);
+ }};
+
+ /**
+ * Callbacks invoked upon state changes in our {@code CameraCaptureSession}. <p>These are run on
+ * {@code mBackgroundThread}.</p>
+ */
+ final CameraCaptureSession.StateListener mCaptureSessionListener =
+ new CameraCaptureSession.StateListener() {
+ @Override
+ public void onConfigured(CameraCaptureSession session) {
+ Log.i(TAG, "Finished configuring camera outputs");
+ mCaptureSession = session;
+
+ SurfaceHolder holder = mSurfaceView.getHolder();
+ if (holder != null) {
+ try {
+ // Build a request for preview footage
+ CaptureRequest.Builder requestBuilder =
+ mCamera.createCaptureRequest(mCamera.TEMPLATE_PREVIEW);
+ requestBuilder.addTarget(holder.getSurface());
+ CaptureRequest previewRequest = requestBuilder.build();
+
+ // Start displaying preview images
+ try {
+ session.setRepeatingRequest(previewRequest, /*listener*/null,
+ /*handler*/null);
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Failed to make repeating preview request", ex);
+ }
+ } catch (CameraAccessException ex) {
+ Log.e(TAG, "Failed to build preview request", ex);
+ }
+ }
+ else {
+ Log.e(TAG, "Holder didn't exist when trying to formulate preview request");
+ }
+ }
+
+ @Override
+ public void onClosed(CameraCaptureSession session) {
+ mCaptureSession = null;
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSession session) {
+ Log.e(TAG, "Configuration error on device '" + mCamera.getId());
+ }};
+
+ /**
+ * Callback invoked when we've received a JPEG image from the camera.
+ */
+ final ImageReader.OnImageAvailableListener mImageCaptureListener =
+ new ImageReader.OnImageAvailableListener() {
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ // Save the image once we get a chance
+ mBackgroundHandler.post(new CapturedImageSaver(reader.acquireNextImage()));
+
+ // Control flow continues in CapturedImageSaver#run()
+ }};
+
+ /**
+ * Deferred processor responsible for saving snapshots to disk. <p>This is run on
+ * {@code mBackgroundThread}.</p>
+ */
+ static class CapturedImageSaver implements Runnable {
+ /** The image to save. */
+ private Image mCapture;
+
+ public CapturedImageSaver(Image capture) {
+ mCapture = capture;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Choose an unused filename under the Pictures/ directory
+ File file = File.createTempFile(CAPTURE_FILENAME_PREFIX, ".jpg",
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES));
+ try (FileOutputStream ostream = new FileOutputStream(file)) {
+ Log.i(TAG, "Retrieved image is" +
+ (mCapture.getFormat() == ImageFormat.JPEG ? "" : "n't") + " a JPEG");
+ ByteBuffer buffer = mCapture.getPlanes()[0].getBuffer();
+ Log.i(TAG, "Captured image size: " +
+ mCapture.getWidth() + 'x' + mCapture.getHeight());
+
+ // Write the image out to the chosen file
+ byte[] jpeg = new byte[buffer.remaining()];
+ buffer.get(jpeg);
+ ostream.write(jpeg);
+ } catch (FileNotFoundException ex) {
+ Log.e(TAG, "Unable to open output file for writing", ex);
+ } catch (IOException ex) {
+ Log.e(TAG, "Failed to write the image to the output file", ex);
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "Unable to create a new output file", ex);
+ } finally {
+ mCapture.close();
+ }
+ }
+ }
+}
diff --git a/tests/Camera2Tests/CameraToo/tests/Android.mk b/tests/Camera2Tests/CameraToo/tests/Android.mk
new file mode 100644
index 0000000..0b58243
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/tests/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := CameraTooTests
+LOCAL_INSTRUMENTATION_FOR := CameraToo
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target
+
+include $(BUILD_PACKAGE)
diff --git a/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml b/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml
new file mode 100644
index 0000000..30210ba
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.camera2.cameratoo.tests">
+ <uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application android:label="CameraToo">
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.example.android.camera2.cameratoo"
+ android:label="CameraToo tests" />
+</manifest>
diff --git a/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java b/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java
new file mode 100644
index 0000000..3acca5a
--- /dev/null
+++ b/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.camera2.cameratoo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.media.Image;
+import android.os.Environment;
+import android.util.Size;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.example.android.camera2.cameratoo.CameraTooActivity;
+import org.junit.Test;
+
+public class CameraTooTest {
+ private <T> void assertComparatorEq(T lhs, T rhs, Comparator<T> rel) {
+ assertEquals(String.format("%s should be equal to %s", lhs, rhs), rel.compare(lhs, rhs), 0);
+ assertEquals(String.format("%s should be equal to %s (reverse check)", lhs, rhs),
+ rel.compare(rhs, lhs), 0);
+ }
+
+ private <T> void assertComparatorLt(T lhs, T rhs, Comparator<T> rel) {
+ assertTrue(String.format("%s should be less than %s", lhs, rhs), rel.compare(lhs, rhs) < 0);
+ assertTrue(String.format("%s should be less than %s (reverse check)", lhs, rhs),
+ rel.compare(rhs, lhs) > 0);
+ }
+
+ @Test
+ public void compareSizesByArea() {
+ Size empty = new Size(0, 0), fatAndFlat = new Size(100, 0), tallAndThin = new Size(0, 100);
+ Size smallSquare = new Size(4, 4), horizRect = new Size(8, 2), vertRect = new Size(2, 8);
+ Size largeSquare = new Size(5, 5);
+ Comparator<Size> rel = new CameraTooActivity.CompareSizesByArea();
+
+ assertComparatorEq(empty, fatAndFlat, rel);
+ assertComparatorEq(empty, tallAndThin, rel);
+ assertComparatorEq(fatAndFlat, empty, rel);
+ assertComparatorEq(fatAndFlat, tallAndThin, rel);
+ assertComparatorEq(tallAndThin, empty, rel);
+ assertComparatorEq(tallAndThin, fatAndFlat, rel);
+
+ assertComparatorEq(smallSquare, horizRect, rel);
+ assertComparatorEq(smallSquare, vertRect, rel);
+ assertComparatorEq(horizRect, smallSquare, rel);
+ assertComparatorEq(horizRect, vertRect, rel);
+ assertComparatorEq(vertRect, smallSquare, rel);
+ assertComparatorEq(vertRect, horizRect, rel);
+
+ assertComparatorLt(empty, smallSquare, rel);
+ assertComparatorLt(empty, horizRect, rel);
+ assertComparatorLt(empty, vertRect, rel);
+
+ assertComparatorLt(fatAndFlat, smallSquare, rel);
+ assertComparatorLt(fatAndFlat, horizRect, rel);
+ assertComparatorLt(fatAndFlat, vertRect, rel);
+
+ assertComparatorLt(tallAndThin, smallSquare, rel);
+ assertComparatorLt(tallAndThin, horizRect, rel);
+ assertComparatorLt(tallAndThin, vertRect, rel);
+
+ assertComparatorLt(empty, largeSquare, rel);
+ assertComparatorLt(fatAndFlat, largeSquare, rel);
+ assertComparatorLt(tallAndThin, largeSquare, rel);
+ assertComparatorLt(smallSquare, largeSquare, rel);
+ assertComparatorLt(horizRect, largeSquare, rel);
+ assertComparatorLt(vertRect, largeSquare, rel);
+ }
+
+ private void assertOptimalSize(Size[] options, int minWidth, int minHeight, Size expected) {
+ Size verdict = CameraTooActivity.chooseBigEnoughSize(options, minWidth, minHeight);
+ assertEquals(String.format("Expected optimal size %s but got %s", expected, verdict),
+ verdict, expected);
+ }
+
+ @Test
+ public void chooseBigEnoughSize() {
+ Size empty = new Size(0, 0), fatAndFlat = new Size(100, 0), tallAndThin = new Size(0, 100);
+ Size smallSquare = new Size(4, 4), horizRect = new Size(8, 2), vertRect = new Size(2, 8);
+ Size largeSquare = new Size(5, 5);
+ Size[] siz =
+ { empty, fatAndFlat, tallAndThin, smallSquare, horizRect, vertRect, largeSquare };
+
+ assertOptimalSize(siz, 0, 0, empty);
+
+ assertOptimalSize(siz, 1, 0, fatAndFlat);
+ assertOptimalSize(siz, 0, 1, tallAndThin);
+
+ assertOptimalSize(siz, 4, 4, smallSquare);
+ assertOptimalSize(siz, 1, 1, smallSquare);
+ assertOptimalSize(siz, 2, 1, smallSquare);
+ assertOptimalSize(siz, 1, 2, smallSquare);
+ assertOptimalSize(siz, 3, 4, smallSquare);
+ assertOptimalSize(siz, 4, 3, smallSquare);
+
+ assertOptimalSize(siz, 8, 2, horizRect);
+ assertOptimalSize(siz, 5, 1, horizRect);
+ assertOptimalSize(siz, 5, 2, horizRect);
+
+ assertOptimalSize(siz, 2, 8, vertRect);
+ assertOptimalSize(siz, 1, 5, vertRect);
+ assertOptimalSize(siz, 2, 5, vertRect);
+
+ assertOptimalSize(siz, 5, 5, largeSquare);
+ assertOptimalSize(siz, 3, 5, largeSquare);
+ assertOptimalSize(siz, 5, 3, largeSquare);
+ }
+
+ private static final FilenameFilter OUTPUT_FILE_DECIDER = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String filename) {
+ return filename.indexOf("cameratoo") == 0 &&
+ filename.indexOf(".jpg") == filename.length() - ".jpg".length();
+ }};
+
+ private static <T> Set<T> newlyAddedElements(Set<T> before, Set<T> after) {
+ Set<T> result = new HashSet<T>(after);
+ result.removeAll(before);
+ return result;
+ }
+
+ @Test
+ public void capturedImageSaver() throws FileNotFoundException, IOException {
+ ByteBuffer buf = ByteBuffer.allocate(25);
+ for(int index = 0; index < buf.capacity(); ++index)
+ buf.put(index, (byte) index);
+
+ Image.Plane plane = mock(Image.Plane.class);
+ when(plane.getBuffer()).thenReturn(buf);
+ when(plane.getPixelStride()).thenReturn(1);
+ when(plane.getRowStride()).thenReturn(5);
+
+ Image.Plane[] onlyPlaneThatMatters = { plane };
+ Image image = mock(Image.class);
+ when(image.getPlanes()).thenReturn(onlyPlaneThatMatters);
+ when(image.getWidth()).thenReturn(5);
+ when(image.getHeight()).thenReturn(5);
+
+ File picturesFolder =
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
+ Set<File> preListing =
+ new HashSet<File>(Arrays.asList(picturesFolder.listFiles(OUTPUT_FILE_DECIDER)));
+
+ CameraTooActivity.CapturedImageSaver saver =
+ new CameraTooActivity.CapturedImageSaver(image);
+ saver.run();
+
+ Set<File> postListing =
+ new HashSet<File>(Arrays.asList(picturesFolder.listFiles(OUTPUT_FILE_DECIDER)));
+ Set<File> newFiles = newlyAddedElements(preListing, postListing);
+
+ assertEquals(newFiles.size(), 1);
+
+ File picture = newFiles.iterator().next();
+ FileInputStream istream = new FileInputStream(picture);
+
+ for(int count = 0; count < buf.capacity(); ++count) {
+ assertEquals(istream.read(), buf.get(count));
+ }
+ assertEquals(istream.read(), -1);
+ assertTrue(picture.delete());
+ }
+}
diff --git a/tests/JobSchedulerTestApp/Android.mk b/tests/JobSchedulerTestApp/Android.mk
new file mode 100644
index 0000000..7336d8c
--- /dev/null
+++ b/tests/JobSchedulerTestApp/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := JobSchedulerTestApp
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/JobSchedulerTestApp/AndroidManifest.xml b/tests/JobSchedulerTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..9654197
--- /dev/null
+++ b/tests/JobSchedulerTestApp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.demo.jobSchedulerApp" >
+
+ <uses-sdk
+ android:minSdkVersion="18"
+ android:targetSdkVersion="18" />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.android.demo.jobSchedulerApp.MainActivity"
+ android:label="@string/app_name"
+ android:windowSoftInputMode="stateHidden" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service
+ android:name=".service.TestJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true"/>
+ </application>
+
+</manifest>
diff --git a/tests/JobSchedulerTestApp/res/drawable-hdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a0f7005
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..a085462
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png
new file mode 100644
index 0000000..4f5d255
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_action_refresh.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..4f78eb8
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png b/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b198ee3
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tests/JobSchedulerTestApp/res/layout/activity_main.xml b/tests/JobSchedulerTestApp/res/layout/activity_main.xml
new file mode 100644
index 0000000..7f4961b
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/layout/activity_main.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="100dp">
+ <TextView
+ android:id="@+id/onstart_textview"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="@color/none_received"
+ android:gravity="center"
+ android:text="@string/onstarttask"/>
+ <TextView
+ android:id="@+id/onstop_textview"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="@color/none_received"
+ android:gravity="center"
+ android:text="@string/onstoptask"/>
+ </LinearLayout>
+ <Button
+ android:id="@+id/finished_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="20dp"
+ android:layout_marginBottom="5dp"
+ android:onClick="finishJob"
+ android:text="@string/finish_job_button_text"/>
+
+ <TextView
+ android:id="@+id/task_params"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/defaultparamtext"
+ android:gravity="center"
+ android:textSize="20dp"
+
+ android:padding="15dp"
+ android:layout_marginBottom="10dp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/constraints"
+ android:textSize="18dp"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dp">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/connectivity"
+ android:layout_marginRight="10dp"/>
+ <RadioGroup
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <RadioButton android:id="@+id/checkbox_any"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/any"/>
+ <RadioButton android:id="@+id/checkbox_unmetered"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/unmetered"/>
+ </RadioGroup>
+
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/timing"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="15dp"
+ android:textSize="17dp"
+ android:text="@string/delay"/>
+ <EditText
+ android:id="@+id/delay_time"
+ android:layout_width="60dp"
+ android:layout_height="wrap_content"
+ android:inputType="number"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/deadline"
+ android:textSize="17dp"/>
+ <EditText
+ android:id="@+id/deadline_time"
+ android:layout_width="60dp"
+ android:layout_height="wrap_content"
+ android:inputType="number"/>
+ </LinearLayout>
+
+ </LinearLayout>
+ <Button
+ android:id="@+id/schedule_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="40dp"
+ android:onClick="scheduleJob"
+ android:text="@string/schedule_job_button_text"/>
+ </LinearLayout>
+</LinearLayout>
diff --git a/tests/JobSchedulerTestApp/res/values-v11/styles.xml b/tests/JobSchedulerTestApp/res/values-v11/styles.xml
new file mode 100644
index 0000000..ff653017
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/values-v11/styles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2013 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>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/JobSchedulerTestApp/res/values-v14/styles.xml b/tests/JobSchedulerTestApp/res/values-v14/styles.xml
new file mode 100644
index 0000000..a4a443a
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/values-v14/styles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2013 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>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/JobSchedulerTestApp/res/values/color.xml b/tests/JobSchedulerTestApp/res/values/color.xml
new file mode 100644
index 0000000..7bd3a91
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/values/color.xml
@@ -0,0 +1,21 @@
+<?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>
+ <color name="none_received">#999999</color>
+ <color name="start_received">#00FF00</color>
+ <color name="stop_received">#FF0000</color>
+</resources>
\ No newline at end of file
diff --git a/tests/JobSchedulerTestApp/res/values/strings.xml b/tests/JobSchedulerTestApp/res/values/strings.xml
new file mode 100644
index 0000000..824d4b1
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/values/strings.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2013 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>
+ <string name="onstoptask">onStopTask</string>
+ <string name="onstarttask">onStartTask</string>
+ <string name="defaultparamtext">task params will show up here.</string>
+ <string name="schedule_job_button_text">Schedule Job</string>
+ <string name="app_name">Job Scheduler Test</string>
+ <string name="finish_job_button_text">taskFinished</string>
+ <string name="manual_sync_text">Manual Sync</string>
+ <string name="constraints">Constraints</string>
+ <string name="connectivity">Connectivity:</string>
+ <string name="any">Any</string>
+ <string name="unmetered">WiFi</string>
+ <string name="timing">Timing:</string>
+ <string name="delay">Delay:</string>
+ <string name="deadline">Deadline:</string>
+</resources>
diff --git a/tests/JobSchedulerTestApp/res/values/styles.xml b/tests/JobSchedulerTestApp/res/values/styles.xml
new file mode 100644
index 0000000..43a8f2b
--- /dev/null
+++ b/tests/JobSchedulerTestApp/res/values/styles.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2013 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>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java
new file mode 100644
index 0000000..15050ef
--- /dev/null
+++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2013 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.demo.jobSchedulerApp;
+
+import android.app.Activity;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.RadioButton;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.demo.jobSchedulerApp.service.TestJobService;
+
+public class MainActivity extends Activity {
+
+ private static final String TAG = "MainActivity";
+
+ public static final int MSG_UNCOLOUR_START = 0;
+ public static final int MSG_UNCOLOUR_STOP = 1;
+ public static final int MSG_SERVICE_OBJ = 2;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ Resources res = getResources();
+ defaultColor = res.getColor(R.color.none_received);
+ startJobColor = res.getColor(R.color.start_received);
+ stopJobColor = res.getColor(R.color.stop_received);
+
+ // Set up UI.
+ mShowStartView = (TextView) findViewById(R.id.onstart_textview);
+ mShowStopView = (TextView) findViewById(R.id.onstop_textview);
+ mParamsTextView = (TextView) findViewById(R.id.task_params);
+ mDelayEditText = (EditText) findViewById(R.id.delay_time);
+ mDeadlineEditText = (EditText) findViewById(R.id.deadline_time);
+ mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered);
+ mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any);
+
+ mServiceComponent = new ComponentName(this, TestJobService.class);
+ // Start service and provide it a way to communicate with us.
+ Intent startServiceIntent = new Intent(this, TestJobService.class);
+ startServiceIntent.putExtra("messenger", new Messenger(mHandler));
+ startService(startServiceIntent);
+ }
+ // UI fields.
+ int defaultColor;
+ int startJobColor;
+ int stopJobColor;
+
+ TextView mShowStartView;
+ TextView mShowStopView;
+ TextView mParamsTextView;
+ EditText mDelayEditText;
+ EditText mDeadlineEditText;
+ RadioButton mWiFiConnectivityRadioButton;
+ RadioButton mAnyConnectivityRadioButton;
+ ComponentName mServiceComponent;
+ /** Service object to interact scheduled jobs. */
+ TestJobService mTestService;
+
+ private static int kJobId = 0;
+
+ Handler mHandler = new Handler(/* default looper */) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UNCOLOUR_START:
+ mShowStartView.setBackgroundColor(defaultColor);
+ break;
+ case MSG_UNCOLOUR_STOP:
+ mShowStopView.setBackgroundColor(defaultColor);
+ break;
+ case MSG_SERVICE_OBJ:
+ mTestService = (TestJobService) msg.obj;
+ mTestService.setUiCallback(MainActivity.this);
+ }
+ }
+ };
+
+ private boolean ensureTestService() {
+ if (mTestService == null) {
+ Toast.makeText(MainActivity.this, "Service null, never got callback?",
+ Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * UI onclick listener to schedule a job. What this job is is defined in
+ * TestJobService#scheduleJob()
+ */
+ public void scheduleJob(View v) {
+ if (!ensureTestService()) {
+ return;
+ }
+
+ JobInfo.Builder builder = new JobInfo.Builder(kJobId++, mServiceComponent);
+
+ String delay = mDelayEditText.getText().toString();
+ if (delay != null && !TextUtils.isEmpty(delay)) {
+ builder.setMinimumLatency(Long.valueOf(delay));
+ }
+ String deadline = mDeadlineEditText.getText().toString();
+ if (deadline != null && !TextUtils.isEmpty(deadline)) {
+ builder.setOverrideDeadline(Long.valueOf(deadline));
+ }
+ boolean requiresUnmetered = mWiFiConnectivityRadioButton.isSelected();
+ boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isSelected();
+ if (requiresUnmetered) {
+ builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED);
+ } else if (requiresAnyConnectivity) {
+ builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY);
+ }
+
+ mTestService.scheduleJob(builder.build());
+
+ }
+
+ /**
+ * UI onclick listener to call jobFinished() in our service.
+ */
+ public void finishJob(View v) {
+ if (!ensureTestService()) {
+ return;
+ }
+ mTestService.callJobFinished();
+ mParamsTextView.setText("");
+ }
+
+ public void onReceivedStartJob(JobParameters params) {
+ mShowStartView.setBackgroundColor(startJobColor);
+ Message m = Message.obtain(mHandler, MSG_UNCOLOUR_START);
+ mHandler.sendMessageDelayed(m, 1000L); // uncolour in 1 second.
+ mParamsTextView.setText("Executing: " + params.getJobId() + " " + params.getExtras());
+ }
+
+ public void onReceivedStopJob() {
+ mShowStopView.setBackgroundColor(stopJobColor);
+ Message m = Message.obtain(mHandler, MSG_UNCOLOUR_STOP);
+ mHandler.sendMessageDelayed(m, 2000L); // uncolour in 1 second.
+ mParamsTextView.setText("");
+ }
+}
diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java
new file mode 100644
index 0000000..bf8e887
--- /dev/null
+++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/service/TestJobService.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * 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.demo.jobSchedulerApp.service;
+
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.demo.jobSchedulerApp.MainActivity;
+
+import java.util.LinkedList;
+
+
+/**
+ * Service to handle sync requests.
+ * <p>
+ * This service is invoked in response to Intents with action android.content.SyncAdapter, and
+ * returns a Binder connection to SyncAdapter.
+ * <p>
+ * For performance, only one sync adapter will be initialized within this application's context.
+ * <p>
+ * Note: The SyncService itself is not notified when a new sync occurs. It's role is to manage the
+ * lifecycle of our and provide a handle to said SyncAdapter to the OS on
+ * request.
+ */
+public class TestJobService extends JobService {
+ private static final String TAG = "SyncService";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i(TAG, "Service created");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.i(TAG, "Service destroyed");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Messenger callback = intent.getParcelableExtra("messenger");
+ Message m = Message.obtain();
+ m.what = MainActivity.MSG_SERVICE_OBJ;
+ m.obj = this;
+ try {
+ callback.send(m);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error passing service object back to activity.");
+ }
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ jobParamsMap.add(params);
+ if (mActivity != null) {
+ mActivity.onReceivedStartJob(params);
+ }
+ Log.i(TAG, "on start job: " + params.getJobId());
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ jobParamsMap.remove(params);
+ mActivity.onReceivedStopJob();
+ Log.i(TAG, "on stop job: " + params.getJobId());
+ return true;
+ }
+
+ MainActivity mActivity;
+ private final LinkedList<JobParameters> jobParamsMap = new LinkedList<JobParameters>();
+
+ public void setUiCallback(MainActivity activity) {
+ mActivity = activity;
+ }
+
+ /** Send job to the JobScheduler. */
+ public void scheduleJob(JobInfo t) {
+ Log.d(TAG, "Scheduling job");
+ JobScheduler tm =
+ (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ tm.schedule(t);
+ }
+
+ public boolean callJobFinished() {
+ JobParameters params = jobParamsMap.poll();
+ if (params == null) {
+ return false;
+ } else {
+ jobFinished(params, false);
+ return true;
+ }
+ }
+
+}
diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
index c7715ad..fc5426c 100644
--- a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
+++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java
@@ -43,7 +43,6 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- HardwareRenderer.sUseRenderThread = true;
setContentView(R.layout.activity_main);
ListView lv = (ListView) findViewById(android.R.id.list);
lv.setDrawSelectorOnTop(true);
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 28c5f33..db2efc3 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -17,12 +17,31 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test.dynamic" >
+
<uses-sdk android:minSdkVersion="20" />
<application
android:hardwareAccelerated="true"
android:label="vector" >
<activity
+ android:name="VectorDrawablePerformance"
+ android:label="Vector Performance" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="com.android.test.dynamic.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name="VectorDrawableAnimation"
+ android:label="VectorTestAnimation" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="com.android.test.dynamic.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
android:name="VectorDrawableTest"
android:label="Vector Icon" >
<intent-filter>
@@ -33,6 +52,15 @@
</intent-filter>
</activity>
<activity
+ android:name="AnimatedVectorDrawableTest"
+ android:label="AnimatedVectorDrawableTest" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="com.android.test.dynamic.TEST" />
+ </intent-filter>
+ </activity>
+ <activity
android:name="VectorDrawable01"
android:label="VectorTest1" >
<intent-filter>
@@ -41,19 +69,7 @@
<category android:name="com.android.test.dynamic.TEST" />
</intent-filter>
</activity>
-
- <activity
- android:name="VectorDrawablePerformance"
- android:label="Vector Performance" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="com.android.test.dynamic.TEST" />
- </intent-filter>
-
- </activity>
-
- <activity
+ <activity
android:name="VectorDrawableDupPerf"
android:label="Vector Performance of clones" >
<intent-filter>
@@ -61,7 +77,6 @@
<category android:name="com.android.test.dynamic.TEST" />
</intent-filter>
-
</activity>
<activity
android:name="VectorDrawableStaticPerf"
@@ -71,9 +86,7 @@
<category android:name="com.android.test.dynamic.TEST" />
</intent-filter>
-
</activity>
-
<activity
android:name="VectorCheckbox"
android:label="On a Checkbox" >
@@ -82,7 +95,6 @@
<category android:name="com.android.test.dynamic.TEST" />
</intent-filter>
-
</activity>
<activity
android:name="VectorPathChecking"
@@ -92,7 +104,6 @@
<category android:name="com.android.test.dynamic.TEST" />
</intent-filter>
-
</activity>
</application>
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation01.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation01.xml
new file mode 100644
index 0000000..d47e019
--- /dev/null
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation01.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <set android:ordering="sequentially" >
+ <objectAnimator
+ android:duration="5000"
+ android:propertyName="trimPathEnd"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ <objectAnimator
+ android:duration="5000"
+ android:propertyName="trimPathEnd"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType" />
+ </set>
+
+</set>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation02.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation02.xml
new file mode 100644
index 0000000..3bf2865
--- /dev/null
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation02.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="5000"
+ android:propertyName="fill"
+ android:valueFrom="#FF000000"
+ android:valueTo="#FFFF0000"/>
+
+</set>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
new file mode 100644
index 0000000..72beba2
--- /dev/null
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="6000"
+ android:propertyName="rotation"
+ android:valueFrom="0"
+ android:valueTo="360"/>
+
+</set>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
new file mode 100644
index 0000000..ff86668
--- /dev/null
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ android:duration="9000"
+ android:propertyName="rotation"
+ android:valueFrom="0"
+ android:valueTo="360"/>
+
+</set>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/animation_drawable_vector.xml b/tests/VectorDrawableTest/res/drawable/animation_drawable_vector.xml
new file mode 100644
index 0000000..a588960
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/animation_drawable_vector.xml
@@ -0,0 +1,36 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_drawable_vector" android:oneshot="false">
+ <item android:drawable="@drawable/vector_drawable01" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable02" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable03" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable04" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable05" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable06" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable07" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable08" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable09" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable10" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable11" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable12" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable13" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable14" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable15" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable16" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable17" android:duration="300" />
+ <item android:drawable="@drawable/vector_drawable18" android:duration="300" />
+ </animation-list>
diff --git a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
new file mode 100644
index 0000000..b8681b6
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
@@ -0,0 +1,36 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/vector_drawable12" >
+
+ <target
+ android:name="pie1"
+ android:animation="@anim/trim_path_animation01" />
+ <target
+ android:name="v"
+ android:animation="@anim/trim_path_animation02" />
+
+ <target
+ android:name="rotationGroup"
+ android:animation="@anim/trim_path_animation03" />
+ <target
+ android:name="rotationGroup3"
+ android:animation="@anim/trim_path_animation03" />
+ <target
+ android:name="rotationGroupBlue"
+ android:animation="@anim/trim_path_animation03" />
+
+</animated-vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
index 850de28..ab5f7f4 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
@@ -25,10 +25,12 @@
<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:fill="#00000000"
android:stroke="#FF000000"
android:strokeWidth="30.65500000000000"/>
<path android:pathData="M 365.015 311.066"
android:name="path2453"
+ android:fill="#00000000"
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"
@@ -38,10 +40,12 @@
android:strokeWidth="30.655000000000001"/>
<path android:pathData="M 170.515 451.566L 305.61 313.46"
android:name="path2457"
+ android:fill="#00000000"
android:stroke="#000000"
android:strokeWidth="30.655000000000001"/>
<path android:pathData="M 557.968 449.974L 426.515 315.375"
android:name="path2459"
+ android:fill="#00000000"
android:stroke="#000000"
android:strokeWidth="30.655000000000001"/>
</group>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
index d0edd8c..e28ec41 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
@@ -23,9 +23,14 @@
android:viewportHeight="600"
android:viewportWidth="600" />
- <group>
+ <group
+ android:name="rotationGroup"
+ android:pivotX="300.0"
+ android:pivotY="300.0"
+ android:rotation="45.0" >
<path
android:name="pie1"
+ android:fill="#00000000"
android:pathData="M300,70 a230,230 0 1,0 1,0 z"
android:stroke="#FF00FF00"
android:strokeWidth="70"
@@ -35,7 +40,66 @@
<path
android:name="v"
android:fill="#FF00FF00"
- android:pathData="M300,70 l 0,-70 70,70 -70,70z"/>
+ android:pathData="M300,70 l 0,-70 70,70 -70,70z" />
+
+ <group
+ android:name="translateToCenterGroup"
+ android:rotation="0.0"
+ android:translateX="200.0"
+ android:translateY="200.0" >
+ <path
+ android:name="twoLines"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup2"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0" >
+ <path
+ android:name="twoLines1"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroupHalf"
+ android:translateX="65.0"
+ android:translateY="80.0" >
+ <group
+ android:name="rotationGroup3"
+ android:pivotX="-65.0"
+ android:pivotY="-80.0"
+ android:rotation="-45.0" >
+ <path
+ android:name="twoLines2"
+ android:fill="#FF00FF00"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="65.0"
+ android:translateY="80.0" >
+ <group
+ android:name="rotationGroupBlue"
+ android:pivotX="-65.0"
+ android:pivotY="-80.0"
+ android:rotation="-45.0" >
+ <path
+ android:name="twoLines3"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+ </group>
+ </group>
+ </group>
</group>
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
index 1abe1e1..8d4ca61 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
@@ -34,6 +34,7 @@
a25,25 -30 0,1 100,-50 l 50,-25
a25,37 -30 0,1 100,-50 l 50,-25
a25,50 -30 0,1 100,-50 l 50,-25"
+ android:fill="#00000000"
android:stroke="#FF00FF00"
android:strokeWidth="10" />
</group>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
index 8e98d02..c28aff4 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
@@ -25,6 +25,7 @@
<path
android:name="house"
android:pathData="M200,300 Q400,50 600,300 T1000,300"
+ android:fill="#00000000"
android:stroke="#FFFF0000"
android:strokeWidth="10"/>
</group>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
index 6d74ebd..d7042fd 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
@@ -27,6 +27,7 @@
<path
android:name="house"
android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+ android:fill="#00000000"
android:stroke="#FFFFFF00"
android:strokeWidth="10" />
</group>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
index a890fd6..47a9574 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
@@ -29,6 +29,7 @@
android:pathData="M10,300 Q400,550 600,300 T1000,300"
android:pivotX="90"
android:pivotY="100"
+ android:fill="#00000000"
android:stroke="#FFFF0000"
android:strokeWidth="60" />
</group>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml
new file mode 100644
index 0000000..8d38cb5
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable22.xml
@@ -0,0 +1,72 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <size
+ android:height="64dp"
+ android:width="64dp" />
+
+ <viewport
+ android:viewportHeight="400"
+ android:viewportWidth="400" />
+
+ <group android:name="backgroundGroup" >
+ <path
+ android:name="background1"
+ android:fill="#80000000"
+ android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background2"
+ android:fill="#80000000"
+ android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+ </group>
+ <group
+ android:name="translateToCenterGroup"
+ android:translateX="50.0"
+ android:translateY="90.0" >
+ <path
+ android:name="twoLines"
+ android:pathData="M 0,0 v 100 M 0,0 h 100"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0" >
+ <path
+ android:name="twoLines1"
+ android:pathData="M 0,0 v 100 M 0,0 h 100"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="130.0"
+ android:translateY="160.0" >
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines2"
+ android:pathData="M 0,0 v 100 M 0,0 h 100"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+ </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml
new file mode 100644
index 0000000..52acd7a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable23.xml
@@ -0,0 +1,86 @@
+<!--
+ 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:height="64dp"
+ android:width="64dp" />
+
+ <viewport
+ android:viewportHeight="400"
+ android:viewportWidth="400" />
+
+ <group android:name="backgroundGroup" >
+ <path
+ android:name="background1"
+ android:fill="#80000000"
+ android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background2"
+ android:fill="#80000000"
+ android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+ </group>
+ <group
+ android:name="translateToCenterGroup"
+ android:translateX="50.0"
+ android:translateY="90.0" >
+ <path
+ android:name="twoLines"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0" >
+ <path
+ android:name="twoLines1"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="130.0"
+ android:translateY="160.0" >
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines3"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+
+ <group
+ android:name="translateGroupHalf"
+ android:translateX="65.0"
+ android:translateY="80.0" >
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines2"
+ android:pathData="@string/twoLinePathData"
+ android:fill="?android:attr/colorForeground"
+ android:stroke="?android:attr/colorForeground"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+ </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml
new file mode 100644
index 0000000..c062d70
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <size
+ android:height="64dp"
+ android:width="64dp" />
+
+ <viewport
+ android:viewportHeight="400"
+ android:viewportWidth="400" />
+
+ <group android:name="backgroundGroup"
+ android:alpha = "0.5" >
+ <path
+ android:name="background1"
+ android:fill="#FF000000"
+ android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+ <path
+ android:name="background2"
+ android:fill="#FF000000"
+ android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+ </group>
+ <group
+ android:name="translateToCenterGroup"
+ android:translateX="50.0"
+ android:translateY="90.0"
+ android:alpha = "0.5" >
+ <path
+ android:name="twoLines"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FFFF0000"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="rotationGroup"
+ android:pivotX="0.0"
+ android:pivotY="0.0"
+ android:rotation="-45.0"
+ android:alpha = "0.5" >
+ <path
+ android:name="twoLines1"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF00FF00"
+ android:strokeWidth="20" />
+
+ <group
+ android:name="translateGroup"
+ android:translateX="130.0"
+ android:translateY="160.0"
+ android:alpha = "0.5">
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines3"
+ android:pathData="@string/twoLinePathData"
+ android:stroke="#FF0000FF"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+
+ <group
+ android:name="translateGroupHalf"
+ android:translateX="65.0"
+ android:translateY="80.0"
+ android:alpha = "0.5">
+ <group android:name="scaleGroup" >
+ <path
+ android:name="twoLines2"
+ android:pathData="@string/twoLinePathData"
+ android:fill="?android:attr/colorForeground"
+ android:stroke="?android:attr/colorForeground"
+ android:strokeWidth="20" />
+ </group>
+ </group>
+ </group>
+ </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test01.xml b/tests/VectorDrawableTest/res/drawable/vector_test01.xml
index a9091ab..fc2a15c 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_test01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_test01.xml
@@ -28,7 +28,8 @@
android:name="002b"
android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
android:stroke="#FF0000FF"
- android:strokeWidth="4" />
+ android:strokeWidth="4"
+ android:fill="#00000000" />
</group>
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test02.xml b/tests/VectorDrawableTest/res/drawable/vector_test02.xml
index ab58c06..9f4abbf 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_test02.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_test02.xml
@@ -28,7 +28,8 @@
android:name="002b"
android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
android:stroke="#FF0000FF"
- android:strokeWidth="4" />
+ android:strokeWidth="4"
+ android:fill="#00000000" />
</group>
</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/values/strings.xml b/tests/VectorDrawableTest/res/values/strings.xml
index 64163c2..b49a1aa 100644
--- a/tests/VectorDrawableTest/res/values/strings.xml
+++ b/tests/VectorDrawableTest/res/values/strings.xml
@@ -15,4 +15,5 @@
-->
<resources>
+ <string name="twoLinePathData" >"M 0,0 v 100 M 0,0 h 100"</string>
</resources>
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
new file mode 100644
index 0000000..6e864fa
--- /dev/null
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.test.dynamic;
+
+import android.app.Activity;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+public class AnimatedVectorDrawableTest extends Activity {
+ private static final String LOGCAT = "VectorDrawableAnimationTest";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Button button = new Button(this);
+ button.setBackgroundResource(R.drawable.animation_vector_drawable01);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AnimatedVectorDrawable frameAnimation = (AnimatedVectorDrawable) v.getBackground();
+ frameAnimation.start();
+ }
+ });
+
+ setContentView(button);
+ }
+}
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
index 7ba01b1..a23d819 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawable01.java
@@ -61,6 +61,8 @@
button.setWidth(200);
button.setBackgroundResource(icon[i]);
container.addView(button);
+ VectorDrawable vd = (VectorDrawable) button.getBackground();
+ vd.setAlpha((i + 1) * (0xFF / (icon.length + 1)));
}
setContentView(container);
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java
new file mode 100644
index 0000000..93b06b6
--- /dev/null
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawableAnimation.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.test.dynamic;
+
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.graphics.drawable.AnimationDrawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+
+public class VectorDrawableAnimation extends Activity {
+ private static final String LOGCAT = "VectorDrawableAnimation";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Button button = new Button(this);
+ button.setBackgroundResource(R.drawable.animation_drawable_vector);
+
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ AnimationDrawable frameAnimation = (AnimationDrawable) v.getBackground();
+ // Start the animation (looped playback by default).
+ frameAnimation.start();
+ }
+ });
+
+ setContentView(button);
+ }
+
+}
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
index e0624e5..814deb8 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/VectorDrawablePerformance.java
@@ -17,11 +17,11 @@
import android.content.res.Resources;
import android.graphics.drawable.VectorDrawable;
import android.os.Bundle;
-import android.view.View;
import android.widget.TextView;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.ScrollView;
+
import java.text.DecimalFormat;
@SuppressWarnings({"UnusedDeclaration"})
@@ -48,7 +48,10 @@
R.drawable.vector_drawable18,
R.drawable.vector_drawable19,
R.drawable.vector_drawable20,
- R.drawable.vector_drawable21
+ R.drawable.vector_drawable21,
+ R.drawable.vector_drawable22,
+ R.drawable.vector_drawable23,
+ R.drawable.vector_drawable24,
};
@Override
diff --git a/tests/VoiceEnrollment/Android.mk b/tests/VoiceEnrollment/Android.mk
new file mode 100644
index 0000000..2ab3d02
--- /dev/null
+++ b/tests/VoiceEnrollment/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := VoiceEnrollment
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+include $(BUILD_PACKAGE)
diff --git a/tests/VoiceEnrollment/AndroidManifest.xml b/tests/VoiceEnrollment/AndroidManifest.xml
new file mode 100644
index 0000000..6321222
--- /dev/null
+++ b/tests/VoiceEnrollment/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.voiceenrollment">
+
+ <application
+ android:permission="android.permission.MANAGE_VOICE_KEYPHRASES">
+ <activity android:name="TestEnrollmentActivity" android:label="Voice Enrollment Application"
+ android:theme="@android:style/Theme.Material.Light.Voice">
+ <intent-filter>
+ <action android:name="com.android.intent.action.MANAGE_VOICE_KEYPHRASES" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <meta-data android:name="android.voice_enrollment"
+ android:resource="@xml/enrollment_application"/>
+ </application>
+</manifest>
diff --git a/tests/VoiceEnrollment/res/xml/enrollment_application.xml b/tests/VoiceEnrollment/res/xml/enrollment_application.xml
new file mode 100644
index 0000000..710a0ac
--- /dev/null
+++ b/tests/VoiceEnrollment/res/xml/enrollment_application.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<voice-enrollment-application xmlns:android="http://schemas.android.com/apk/res/android"
+ android:searchKeyphraseId="101"
+ android:searchKeyphrase="Hello There"
+ android:searchKeyphraseSupportedLocales="en-US,en-GB,fr-FR,de-DE" />
diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
new file mode 100644
index 0000000..7fbd965
--- /dev/null
+++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceenrollment;
+
+import android.app.Activity;
+
+public class TestEnrollmentActivity extends Activity {
+ // TODO(sansid): Add a test enrollment flow here.
+}
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
index e1a5854..33f000d 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -3,7 +3,7 @@
<application>
<activity android:name="VoiceInteractionMain" android:label="Voice Interaction"
- android:theme="@android:style/Theme.Quantum">
+ android:theme="@android:style/Theme.Material">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
@@ -24,7 +24,7 @@
android:process=":session">
</service>
<activity android:name="TestInteractionActivity" android:label="Voice Interaction Target"
- android:theme="@android:style/Theme.Quantum.Light.Voice">
+ android:theme="@android:style/Theme.Material.Light.Voice">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
index d40b05f..00c2c64 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -21,6 +21,8 @@
import android.service.voice.VoiceInteractionService;
import android.util.Log;
+import java.util.Arrays;
+
public class MainInteractionService extends VoiceInteractionService {
static final String TAG = "MainInteractionService";
@@ -28,6 +30,9 @@
public void onCreate() {
super.onCreate();
Log.i(TAG, "Creating " + this);
+ Log.i(TAG, "Keyphrase enrollment error? " + getKeyphraseEnrollmentInfo().getParseError());
+ Log.i(TAG, "Keyphrase enrollment meta-data: "
+ + Arrays.toString(getKeyphraseEnrollmentInfo().getKeyphrases()));
}
@Override
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 12d5389..2028ff4 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1457,9 +1457,9 @@
if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
// See if there is a better density resource
if (mconfig.density < bestDensity &&
- mconfig.density > preferredDensity &&
+ mconfig.density >= preferredDensity &&
bestDensity > preferredDensity) {
- // This density is between our best density and
+ // This density is our preferred density, or between our best density and
// the preferred density, therefore it is better.
bestDensity = mconfig.density;
} else if (mconfig.density > bestDensity &&
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 5a60014..322d86c 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -105,20 +105,12 @@
" en\n"
" port,en\n"
" port,land,en_US\n"
- " If you put the special locale, zz_ZZ on the list, it will perform\n"
- " pseudolocalization on the default locale, modifying all of the\n"
- " strings so you can look for strings that missed the\n"
- " internationalization process. For example:\n"
- " port,land,zz_ZZ\n"
" -d one or more device assets to include, separated by commas\n"
" -f force overwrite of existing files\n"
" -g specify a pixel tolerance to force images to grayscale, default 0\n"
" -j specify a jar or zip file containing classes to include\n"
" -k junk path of file(s) added\n"
" -m make package directories under location specified by -J\n"
-#if 0
- " -p pseudolocalize the default configuration\n"
-#endif
" -u update existing packages (add new, replace older, remove deleted files)\n"
" -v verbose output\n"
" -x create extending (non-application) resource IDs\n"
@@ -141,6 +133,8 @@
" manifest, making the application debuggable even on production devices.\n"
" --include-meta-data\n"
" when used with \"dump badging\" also includes meta-data tags.\n"
+ " --pseudo-localize\n"
+ " generate resources for pseudo-locales (en-XA and ar-XB).\n"
" --min-sdk-version\n"
" inserts android:minSdkVersion in to manifest. If the version is 7 or\n"
" higher, the default encoding for resources will be in UTF-8.\n"
@@ -647,6 +641,8 @@
goto bail;
}
gUserIgnoreAssets = argv[0];
+ } else if (strcmp(cp, "-pseudo-localize") == 0) {
+ bundle.setPseudolocalize(PSEUDO_ACCENTED | PSEUDO_BIDI);
} else {
fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
wantUsage = true;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index e599643..f10904c 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -166,6 +166,35 @@
ResTable_config mParams;
};
+class AnnotationProcessor {
+public:
+ AnnotationProcessor() : mDeprecated(false), mSystemApi(false) { }
+
+ void preprocessComment(String8& comment) {
+ if (comment.size() > 0) {
+ if (comment.contains("@deprecated")) {
+ mDeprecated = true;
+ }
+ if (comment.removeAll("@SystemApi")) {
+ mSystemApi = true;
+ }
+ }
+ }
+
+ void printAnnotations(FILE* fp, const char* indentStr) {
+ if (mDeprecated) {
+ fprintf(fp, "%s@Deprecated\n", indentStr);
+ }
+ if (mSystemApi) {
+ fprintf(fp, "%s@android.annotation.SystemApi\n", indentStr);
+ }
+ }
+
+private:
+ bool mDeprecated;
+ bool mSystemApi;
+};
+
// ==========================================================================
// ==========================================================================
// ==========================================================================
@@ -1742,16 +1771,13 @@
NA = idents.size();
- bool deprecated = false;
-
String16 comment = symbols->getComment(realClassName);
+ AnnotationProcessor ann;
fprintf(fp, "%s/** ", indentStr);
if (comment.size() > 0) {
String8 cmt(comment);
+ ann.preprocessComment(cmt);
fprintf(fp, "%s\n", cmt.string());
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
} else {
fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
}
@@ -1823,9 +1849,7 @@
}
fprintf(fp, "%s */\n", getIndentSpace(indent));
- if (deprecated) {
- fprintf(fp, "%s@Deprecated\n", indentStr);
- }
+ ann.printAnnotations(fp, indentStr);
fprintf(fp,
"%spublic static final int[] %s = {\n"
@@ -1871,17 +1895,14 @@
//printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
// String8(attr16).string(), String8(name16).string(), typeSpecFlags);
const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
-
- bool deprecated = false;
-
+
+ AnnotationProcessor ann;
fprintf(fp, "%s/**\n", indentStr);
if (comment.size() > 0) {
String8 cmt(comment);
+ ann.preprocessComment(cmt);
fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr);
fprintf(fp, "%s %s\n", indentStr, cmt.string());
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
} else {
fprintf(fp,
"%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
@@ -1893,10 +1914,8 @@
}
if (typeComment.size() > 0) {
String8 cmt(typeComment);
+ ann.preprocessComment(cmt);
fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string());
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
}
if (comment.size() > 0) {
if (pub) {
@@ -1915,9 +1934,7 @@
getSymbolPackage(name8, assets, pub).string(),
getSymbolName(name8).string());
fprintf(fp, "%s*/\n", indentStr);
- if (deprecated) {
- fprintf(fp, "%s@Deprecated\n", indentStr);
- }
+ ann.printAnnotations(fp, indentStr);
fprintf(fp,
"%spublic static final int %s_%s = %d;\n",
indentStr, nclassName.string(),
@@ -2056,16 +2073,14 @@
String8 name8(sym.name);
String16 comment(sym.comment);
bool haveComment = false;
- bool deprecated = false;
+ AnnotationProcessor ann;
if (comment.size() > 0) {
haveComment = true;
String8 cmt(comment);
+ ann.preprocessComment(cmt);
fprintf(fp,
"%s/** %s\n",
getIndentSpace(indent), cmt.string());
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
} else if (sym.isPublic && !includePrivate) {
sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
assets->getPackage().string(), className.string(),
@@ -2074,6 +2089,7 @@
String16 typeComment(sym.typeComment);
if (typeComment.size() > 0) {
String8 cmt(typeComment);
+ ann.preprocessComment(cmt);
if (!haveComment) {
haveComment = true;
fprintf(fp,
@@ -2082,16 +2098,11 @@
fprintf(fp,
"%s %s\n", getIndentSpace(indent), cmt.string());
}
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
}
if (haveComment) {
fprintf(fp,"%s */\n", getIndentSpace(indent));
}
- if (deprecated) {
- fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
- }
+ ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, id_format,
getIndentSpace(indent),
flattenSymbol(name8).string(), (int)sym.int32Val);
@@ -2107,25 +2118,21 @@
}
String8 name8(sym.name);
String16 comment(sym.comment);
- bool deprecated = false;
+ AnnotationProcessor ann;
if (comment.size() > 0) {
String8 cmt(comment);
+ ann.preprocessComment(cmt);
fprintf(fp,
"%s/** %s\n"
"%s */\n",
getIndentSpace(indent), cmt.string(),
getIndentSpace(indent));
- if (strstr(cmt.string(), "@deprecated") != NULL) {
- deprecated = true;
- }
} else if (sym.isPublic && !includePrivate) {
sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
assets->getPackage().string(), className.string(),
String8(sym.name).string());
}
- if (deprecated) {
- fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
- }
+ ann.printAnnotations(fp, getIndentSpace(indent));
fprintf(fp, "%spublic static final String %s=\"%s\";\n",
getIndentSpace(indent),
flattenSymbol(name8).string(), sym.stringVal.string());
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index cb68340..08486e6 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -16,6 +16,8 @@
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
+LOCAL_JAVACFLAGS := -source 6 -target 6
+
#
# Define rules to build temp_layoutlib.jar, which contains a subset of
# the classes in framework.jar. The layoutlib_create tool is used to
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk
index e3d48fc..cfd597e 100644
--- a/tools/layoutlib/bridge/Android.mk
+++ b/tools/layoutlib/bridge/Android.mk
@@ -18,6 +18,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_JAVA_RESOURCE_DIRS := resources
+LOCAL_JAVACFLAGS := -source 6 -target 6
LOCAL_JAVA_LIBRARIES := \
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index e35bc06..cd199dc 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -977,7 +977,7 @@
/*package*/ static void native_drawText(long nativeCanvas,
final char[] text, final int index, final int count,
final float startX, final float startY, final int flags, long paint,
- long typeface) {
+ final long typeface) {
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@@ -985,6 +985,11 @@
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
// WARNING: the logic in this method is similar to Paint_Delegate.measureText.
// Any change to this method should be reflected in Paint.measureText
+
+ // assert that the typeface passed is actually the one stored in paint.
+ assert (typeface == paintDelegate.mNativeTypeface);
+
+
// Paint.TextAlign indicates how the text is positioned relative to X.
// LEFT is the default and there's nothing to do.
float x = startX;
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 9ea4538..d73adab 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -21,6 +21,8 @@
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.content.res.AssetManager;
+
import java.awt.Font;
import java.io.File;
import java.util.ArrayList;
@@ -47,7 +49,6 @@
private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf";
private static final String FONT_SUFFIX_BOLD = "Bold.ttf";
private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
- private static final String FONT_SUBSTRING_COMPACT = "UI";
/**
* A class associating {@link Font} with its metadata.
@@ -56,11 +57,6 @@
Font mFont;
/** Regular, Bold, Italic, or BoldItalic. */
int mStyle;
- /**
- * The variant of the Font - compact or elegant.
- * @see Paint#setElegantTextHeight(boolean)
- */
- boolean mIsCompact;
}
// ---- delegate manager ----
@@ -75,6 +71,14 @@
// ---- delegate data ----
private List<FontInfo> mFonts = new ArrayList<FontInfo>();
+ /**
+ * The variant of the Font Family - compact or elegant.
+ * 0 is unspecified, 1 is compact and 2 is elegant. This needs to be kept in sync with values in
+ * android.graphics.FontFamily
+ *
+ * @see Paint#setElegantTextHeight(boolean)
+ */
+ private FontVariant mVariant;
// Path of fonts that haven't been created since sFontLoader hasn't been initialized.
private List<String> mPath = new ArrayList<String>();
@@ -93,37 +97,22 @@
sPostInitDelegate.clear();
}
- public Font getFont(int style, boolean isCompact) {
+ public Font getFont(int style) {
FontInfo plainFont = null;
- FontInfo styledFont = null; // Font matching the style but not isCompact
for (FontInfo font : mFonts) {
if (font.mStyle == style) {
- if (font.mIsCompact == isCompact) {
- return font.mFont;
- }
- styledFont = font;
+ return font.mFont;
}
- if (font.mStyle == Font.PLAIN) {
- if (plainFont == null) {
- plainFont = font;
- continue;
- }
- if (font.mIsCompact == isCompact) {
- // Override the previous selection of plain font since we've found a better one.
- plainFont = font;
- }
+ if (font.mStyle == Font.PLAIN && plainFont == null) {
+ plainFont = font;
}
}
- if (styledFont != null) {
- return styledFont.mFont;
- }
// No font with the mentioned style is found. Try to derive one.
if (plainFont != null && style > 0 && style < 4) {
- styledFont = new FontInfo();
+ FontInfo styledFont = new FontInfo();
styledFont.mFont = plainFont.mFont.deriveFont(style);
styledFont.mStyle = style;
- styledFont.mIsCompact = plainFont.mIsCompact;
// Add the font to the list of fonts so that we don't have to derive it the next time.
mFonts.add(styledFont);
return styledFont.mFont;
@@ -131,11 +120,20 @@
return null;
}
+ public FontVariant getVariant() {
+ return mVariant;
+ }
+
+
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static long nCreateFamily() {
+ /*package*/ static long nCreateFamily(String lang, int variant) {
+ // TODO: support lang. This is required for japanese locale.
FontFamily_Delegate delegate = new FontFamily_Delegate();
+ // variant can be 0, 1 or 2.
+ assert variant < 3;
+ delegate.mVariant = FontVariant.values()[variant];
if (sFontLocation != null) {
delegate.init();
} else {
@@ -164,6 +162,13 @@
return false;
}
+ @LayoutlibDelegate
+ /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "FontFamily.addFontFromAsset is not supported.", null /*throwable*/, null /*data*/);
+ return false;
+ }
+
private void init() {
for (String path : mPath) {
addFont(path);
@@ -195,13 +200,6 @@
style = Font.ITALIC;
}
fontInfo.mStyle = style;
-
- // Names of compact fonts end with UI-<style>.ttf. For example, NotoNakshUI-Regular.ttf.
- // This should go away when this info is passed on by nAddFont().
- int hyphenIndex = fontName.lastIndexOf('-');
- fontInfo.mIsCompact = hyphenIndex > 0 &&
- fontName.substring(0, hyphenIndex).endsWith(FONT_SUBSTRING_COMPACT);
-
}
private static Font loadFont(String path) {
@@ -214,7 +212,7 @@
} catch (Exception e) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
String.format("Unable to load font %1$s", relativePath),
- null /*throwable*/, null /*data*/);
+ e /*throwable*/, null /*data*/);
}
} else {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -224,4 +222,12 @@
return null;
}
+
+
+ // ---- Public helper class ----
+
+ public enum FontVariant {
+ // The order needs to be kept in sync with android.graphics.FontFamily.
+ NONE, COMPACT, ELEGANT
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 911f4e7..6ee307e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -21,6 +21,7 @@
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.graphics.FontFamily_Delegate.FontVariant;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.FontMetricsInt;
import android.text.TextUtils;
@@ -30,7 +31,6 @@
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Toolkit;
-import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.Collections;
@@ -81,7 +81,8 @@
private float mTextScaleX;
private float mTextSkewX;
private int mHintingMode = Paint.HINTING_ON;
- private boolean mIsCompact = true;
+ // Variant of the font.
+ private FontVariant mFontVariant = FontVariant.NONE;
private Xfermode_Delegate mXfermode;
private ColorFilter_Delegate mColorFilter;
@@ -92,6 +93,8 @@
private Locale mLocale = Locale.getDefault();
+ // Used only to assert invariants.
+ public long mNativeTypeface;
// ---- Public Helper methods ----
@@ -437,7 +440,7 @@
/*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
- return delegate != null && !delegate.mIsCompact;
+ return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT;
}
@LayoutlibDelegate
@@ -448,7 +451,7 @@
return;
}
- delegate.mIsCompact = !elegant;
+ delegate.mFontVariant = elegant ? FontVariant.ELEGANT : FontVariant.COMPACT;
}
@LayoutlibDelegate
@@ -887,6 +890,7 @@
}
delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
+ delegate.mNativeTypeface = typeface;
delegate.updateFontObject();
return typeface;
}
@@ -965,15 +969,10 @@
}
@LayoutlibDelegate
- /*package*/ static float native_getTextRunAdvances(long native_object,
- long native_typeface /*ignored*/,
+ /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
char[] text, int index, int count, int contextIndex, int contextCount,
int flags, float[] advances, int advancesIndex) {
- // native_typeface is passed here since Framework's old implementation did not have the
- // typeface object associated with the Paint. Since, we follow the new framework way,
- // we store the typeface with the paint and use it directly.
-
if (advances != null)
for (int i = advancesIndex; i< advancesIndex+count; i++)
advances[i]=0;
@@ -982,6 +981,12 @@
if (delegate == null) {
return 0.f;
}
+
+ // native_typeface is passed here since Framework's old implementation did not have the
+ // typeface object associated with the Paint. Since, we follow the new framework way,
+ // we store the typeface with the paint and use it directly.
+ assert (native_typeface == delegate.mNativeTypeface);
+
boolean isRtl = isRtl(flags);
int limit = index + count;
@@ -1022,37 +1027,41 @@
}
@LayoutlibDelegate
- /*package*/ static void native_getTextPath(long native_object, int bidiFlags,
- char[] text, int index, int count, float x, float y, long path) {
+ /*package*/ static void native_getTextPath(long native_object, long native_typeface,
+ int bidiFlags, char[] text, int index, int count, float x, float y, long path) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Paint.getTextPath is not supported.", null, null /*data*/);
}
@LayoutlibDelegate
- /*package*/ static void native_getTextPath(long native_object, int bidiFlags,
- String text, int start, int end, float x, float y, long path) {
+ /*package*/ static void native_getTextPath(long native_object, long native_typeface,
+ int bidiFlags, String text, int start, int end, float x, float y, long path) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Paint.getTextPath is not supported.", null, null /*data*/);
}
@LayoutlibDelegate
- /*package*/ static void nativeGetStringBounds(long nativePaint, String text, int start,
- int end, int bidiFlags, Rect bounds) {
- nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bidiFlags,
- bounds);
+ /*package*/ static void nativeGetStringBounds(long nativePaint, long native_typeface,
+ String text, int start, int end, int bidiFlags, Rect bounds) {
+ nativeGetCharArrayBounds(nativePaint, native_typeface, text.toCharArray(), start,
+ end - start, bidiFlags, bounds);
}
@LayoutlibDelegate
- /*package*/ static void nativeGetCharArrayBounds(long nativePaint, char[] text, int index,
- int count, int bidiFlags, Rect bounds) {
+ /*package*/ static void nativeGetCharArrayBounds(long nativePaint, long native_typeface,
+ char[] text, int index, int count, int bidiFlags, Rect bounds) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
return;
}
+
+ // assert that the typeface passed is actually the one that we had stored.
+ assert (native_typeface == delegate.mNativeTypeface);
+
delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
}
@@ -1079,6 +1088,7 @@
mJoin = paint.mJoin;
mTextAlign = paint.mTextAlign;
mTypeface = paint.mTypeface;
+ mNativeTypeface = paint.mNativeTypeface;
mStrokeWidth = paint.mStrokeWidth;
mStrokeMiter = paint.mStrokeMiter;
mTextSize = paint.mTextSize;
@@ -1102,6 +1112,7 @@
mJoin = Paint.Join.MITER.nativeInt;
mTextAlign = 0;
mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
+ mNativeTypeface = 0;
mStrokeWidth = 1.f;
mStrokeMiter = 4.f;
mTextSize = 20.f;
@@ -1124,7 +1135,7 @@
private void updateFontObject() {
if (mTypeface != null) {
// Get the fonts from the TypeFace object.
- List<Font> fonts = mTypeface.getFonts(mIsCompact);
+ List<Font> fonts = mTypeface.getFonts(mFontVariant);
// create new font objects as well as FontMetrics, based on the current text size
// and skew info.
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 9746b48..908bb64 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -22,6 +22,7 @@
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.content.res.AssetManager;
+import android.graphics.FontFamily_Delegate.FontVariant;
import java.awt.Font;
import java.io.File;
@@ -69,17 +70,28 @@
return sManager.getDelegate(nativeTypeface);
}
- public List<Font> getFonts(boolean compact) {
+ public List<Font> getFonts(FontVariant variant) {
List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
+ // If we are unable to find fonts matching the variant, we return the fonts from the
+ // other variant since we always want to draw something, rather than nothing.
+ // TODO: check this behaviour with platform.
+ List<Font> otherVariantFonts = new ArrayList<Font>();
for (FontFamily_Delegate ffd : mFontFamilies) {
if (ffd != null) {
- Font font = ffd.getFont(mStyle, compact);
+ Font font = ffd.getFont(mStyle);
if (font != null) {
- fonts.add(font);
+ if (ffd.getVariant() == variant || ffd.getVariant() == FontVariant.NONE) {
+ fonts.add(font);
+ } else {
+ otherVariantFonts.add(font);
+ }
}
}
}
- return fonts;
+ if (fonts.size() > 0) {
+ return fonts;
+ }
+ return otherVariantFonts;
}
// ---- native methods ----
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index af22f44..36102f1 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -151,7 +151,7 @@
@Override
public View inflate(int resource, ViewGroup root) {
Context context = getContext();
- if (context instanceof ContextThemeWrapper) {
+ while (context instanceof ContextThemeWrapper) {
context = ((ContextThemeWrapper) context).getBaseContext();
}
if (context instanceof BridgeContext) {
@@ -217,7 +217,7 @@
private void setupViewInContext(View view, AttributeSet attrs) {
Context context = getContext();
- if (context instanceof ContextThemeWrapper) {
+ while (context instanceof ContextThemeWrapper) {
context = ((ContextThemeWrapper) context).getBaseContext();
}
if (context instanceof BridgeContext) {
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 3bf2b20..2f40003 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -195,8 +195,8 @@
}
@Override
- public IWindowSession openSession(IInputMethodClient arg0, IInputContext arg1)
- throws RemoteException {
+ public IWindowSession openSession(IWindowSessionCallback argn1, IInputMethodClient arg0,
+ IInputContext arg1) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@@ -276,6 +276,11 @@
}
@Override
+ public float getCurrentAnimatorScale() throws RemoteException {
+ return 0;
+ }
+
+ @Override
public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
@@ -419,11 +424,6 @@
}
@Override
- public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
- return false;
- }
-
- @Override
public IBinder asBinder() {
// TODO Auto-generated method stub
return null;
diff --git a/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java
index 0dddf3d..dafc96b 100644
--- a/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java
@@ -48,7 +48,7 @@
AttributeSet attrs) {
if (menuItem instanceof BridgeMenuItemImpl) {
Context context = thisInflater.getContext();
- if (context instanceof ContextThemeWrapper) {
+ while (context instanceof ContextThemeWrapper) {
context = ((ContextThemeWrapper) context).getBaseContext();
}
if (context instanceof BridgeContext) {
diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java b/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java
index cdb839a..8d1d0c1 100644
--- a/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java
@@ -42,7 +42,7 @@
CharSequence title, int showAsAction) {
super(menu, group, id, categoryOrder, ordering, title, showAsAction);
Context context = menu.getContext();
- if (context instanceof ContextThemeWrapper) {
+ while (context instanceof ContextThemeWrapper) {
context = ((ContextThemeWrapper) context).getBaseContext();
}
if (context instanceof BridgeContext) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index ffab4de..cc69af2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -214,7 +214,8 @@
Capability.EXTENDED_VIEWINFO,
Capability.FIXED_SCALABLE_NINE_PATCH,
Capability.RTL,
- Capability.ACTION_BAR);
+ Capability.ACTION_BAR,
+ Capability.SIMULATE_PLATFORM);
BridgeAssetManager.initSystem();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index 0bb7fc2..9ec6f4d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -135,7 +135,6 @@
@Override
public void setImeWindowStatus(IBinder arg0, int arg1, int arg2) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -197,25 +196,25 @@
}
@Override
- public boolean switchToNextInputMethod(IBinder arg0, boolean arg1) throws RemoteException {
+ public boolean switchToNextInputMethod(IBinder arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
@Override
- public boolean shouldOfferSwitchingToNextInputMethod(IBinder arg0) throws RemoteException {
+ public boolean shouldOfferSwitchingToNextInputMethod(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
@Override
- public int getInputMethodWindowVisibleHeight() throws RemoteException {
+ public int getInputMethodWindowVisibleHeight() throws RemoteException {
// TODO Auto-generated method stub
return 0;
}
@Override
- public void notifyTextCommitted() throws RemoteException {
+ public void notifyUserAction(int sequenceNumber) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 00c0f93..17d990b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -33,6 +33,11 @@
}
@Override
+ public boolean isPowerSaveMode() throws RemoteException {
+ return false;
+ }
+
+ @Override
public IBinder asBinder() {
// pass for now.
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 09e6878..e1064b1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -197,7 +197,7 @@
}
@Override
- public void onRectangleOnScreenRequested(IBinder window, Rect rectangle, boolean immediate) {
+ public void onRectangleOnScreenRequested(IBinder window, Rect rectangle) {
// pass for now.
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
index e59ccd7..d95c815 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
@@ -23,10 +23,8 @@
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
-import com.android.ide.common.rendering.api.SystemViewCookie;
import com.android.internal.R;
import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.util.Predicate;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuItemImpl;
import com.android.internal.widget.ActionBarAccessor;
@@ -50,7 +48,6 @@
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ActionMenuView;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
@@ -59,8 +56,6 @@
import java.util.ArrayList;
-import static com.android.ide.common.rendering.api.SystemViewCookie.ACTION_BAR_OVERFLOW;
-
/**
* A layout representing the action bar.
*/
@@ -174,29 +169,6 @@
mActionBarView.setSplitToolbar(mSplit);
inflateMenus();
-
- // Find if the Overflow Menu Button (the three dots) exists. If yes,
- // add the view cookie.
- Predicate<View> overflowMenuButtonTest = new Predicate<View>() {
- @Override
- public boolean apply(View view) {
- ViewGroup.LayoutParams lp = view.getLayoutParams();
- return lp instanceof ActionMenuView.LayoutParams &&
- ((ActionMenuView.LayoutParams) lp).isOverflowButton;
- }
- };
- View overflowMenu = null;
- if (mSplit) {
- if (splitView != null) {
- overflowMenu = splitView.findViewByPredicate(overflowMenuButtonTest);
- }
- }
- else {
- overflowMenu = mActionBarView.findViewByPredicate(overflowMenuButtonTest);
- }
- if (overflowMenu != null) {
- mBridgeContext.addViewKey(overflowMenu, new SystemViewCookie(ACTION_BAR_OVERFLOW));
- }
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 1498044..2421f29 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.bars;
+import com.android.layoutlib.bridge.impl.Config;
import com.android.resources.Density;
import com.android.resources.ResourceType;
@@ -30,14 +31,14 @@
public class StatusBar extends CustomBar {
- public StatusBar(Context context, Density density, int direction, boolean RtlEnabled)
- throws XmlPullParserException {
+ public StatusBar(Context context, Density density, int direction, boolean RtlEnabled,
+ int simulatedPlatformVersion) throws XmlPullParserException {
// FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml");
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
- setBackgroundColor(0xFF000000);
+ setBackgroundColor(Config.getStatusBarColor(simulatedPlatformVersion));
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Config.java
new file mode 100644
index 0000000..e8bc292
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Config.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+/**
+ * Various helper methods to simulate older versions of platform.
+ */
+public class Config {
+
+ public static boolean showOnScreenNavBar(int platformVersion) {
+ // return true if ICS or later.
+ return platformVersion >= 14 || platformVersion == 0;
+ }
+
+ public static int getStatusBarColor(int platformVersion) {
+ // return white for froyo and earlier; black otherwise.
+ return platformVersion >= 9 || platformVersion == 0 ? 0xFF000000 : 0xFFFFFFFF;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 4af73cf..c715003 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -37,6 +37,7 @@
import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.common.rendering.api.ViewType;
import com.android.internal.util.XmlUtils;
import com.android.internal.view.menu.ActionMenuItemView;
import com.android.internal.view.menu.BridgeMenuItemImpl;
@@ -83,9 +84,12 @@
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewParent;
import android.view.WindowManagerGlobal_Delegate;
+import android.view.ViewParent;
import android.widget.AbsListView;
import android.widget.AbsSpinner;
+import android.widget.ActionMenuView;
import android.widget.AdapterView;
import android.widget.ExpandableListView;
import android.widget.FrameLayout;
@@ -267,12 +271,13 @@
mViewRoot = topLayout;
topLayout.setOrientation(LinearLayout.HORIZONTAL);
- try {
- NavigationBar navigationBar = createNavigationBar(context,
- hardwareConfig.getDensity(), isRtl, params.isRtlSupported());
- topLayout.addView(navigationBar);
- } catch (XmlPullParserException ignored) {
-
+ if (Config.showOnScreenNavBar(params.getSimulatedPlatformVersion())) {
+ try {
+ NavigationBar navigationBar = createNavigationBar(context,
+ hardwareConfig.getDensity(), isRtl, params.isRtlSupported());
+ topLayout.addView(navigationBar);
+ } catch (XmlPullParserException ignored) {
+ }
}
}
@@ -325,7 +330,8 @@
// system bar
try {
StatusBar statusBar = createStatusBar(context, hardwareConfig.getDensity(),
- layoutDirection, params.isRtlSupported());
+ layoutDirection, params.isRtlSupported(),
+ params.getSimulatedPlatformVersion());
topLayout.addView(statusBar);
} catch (XmlPullParserException ignored) {
@@ -368,7 +374,8 @@
backgroundLayout.addView(mContentRoot);
}
- if (mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
+ if (Config.showOnScreenNavBar(params.getSimulatedPlatformVersion()) &&
+ mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
mNavigationBarSize > 0) {
// system bar
try {
@@ -1473,16 +1480,49 @@
ViewInfo result;
if (isContentFrame) {
+ // The view is part of the layout added by the user. Hence,
+ // the ViewCookie may be obtained only through the Context.
result = new ViewInfo(view.getClass().getName(),
- getViewKey(view),
+ getContext().getViewKey(view),
view.getLeft(), view.getTop() + offset, view.getRight(),
view.getBottom() + offset, view, view.getLayoutParams());
-
} else {
- result = new SystemViewInfo(view.getClass().getName(),
+ // We are part of the system decor.
+ SystemViewInfo r = new SystemViewInfo(view.getClass().getName(),
getViewKey(view),
view.getLeft(), view.getTop(), view.getRight(),
view.getBottom(), view, view.getLayoutParams());
+ result = r;
+ // We currently mark three kinds of views:
+ // 1. Menus in the Action Bar
+ // 2. Menus in the Overflow popup.
+ // 3. The overflow popup button.
+ if (view instanceof ListMenuItemView) {
+ // Mark 2.
+ // All menus in the popup are of type ListMenuItemView.
+ r.setViewType(ViewType.ACTION_BAR_OVERFLOW_MENU);
+ } else {
+ // Mark 3.
+ ViewGroup.LayoutParams lp = view.getLayoutParams();
+ if (lp instanceof ActionMenuView.LayoutParams &&
+ ((ActionMenuView.LayoutParams) lp).isOverflowButton) {
+ r.setViewType(ViewType.ACTION_BAR_OVERFLOW);
+ } else {
+ // Mark 1.
+ // A view is a menu in the Action Bar is it is not the overflow button and of
+ // its parent is of type ActionMenuView. We can also check if the view is
+ // instanceof ActionMenuItemView but that will fail for menus using
+ // actionProviderClass.
+ ViewParent parent = view.getParent();
+ while (parent != mViewRoot && parent instanceof ViewGroup) {
+ if (parent instanceof ActionMenuView) {
+ r.setViewType(ViewType.ACTION_BAR_MENU);
+ break;
+ }
+ parent = parent.getParent();
+ }
+ }
+ }
}
if (setExtendedInfo) {
@@ -1501,7 +1541,7 @@
return result;
}
- /**
+ /* (non-Javadoc)
* The cookie for menu items are stored in menu item and not in the map from View stored in
* BridgeContext.
*/
@@ -1535,9 +1575,9 @@
* Creates the status bar with wifi and battery icons.
*/
private StatusBar createStatusBar(BridgeContext context, Density density, int direction,
- boolean isRtlSupported) throws XmlPullParserException {
+ boolean isRtlSupported, int platformVersion) throws XmlPullParserException {
StatusBar statusBar = new StatusBar(context, density,
- direction, isRtlSupported);
+ direction, isRtlSupported, platformVersion);
statusBar.setLayoutParams(
new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, mStatusBarSize));
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java
index 5c267df..9fea167 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java
@@ -17,9 +17,15 @@
package com.android.layoutlib.bridge.impl;
import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.ide.common.rendering.api.ViewType;
+/**
+ * ViewInfo for views added by the platform.
+ */
public class SystemViewInfo extends ViewInfo {
+ private ViewType mViewType;
+
public SystemViewInfo(String name, Object cookie, int left, int top,
int right, int bottom) {
super(name, cookie, left, top, right, bottom);
@@ -32,7 +38,14 @@
}
@Override
- public boolean isSystemView() {
- return true;
+ public ViewType getViewType() {
+ if (mViewType != null) {
+ return mViewType;
+ }
+ return ViewType.SYSTEM_UNKNOWN;
+ }
+
+ public void setViewType(ViewType type) {
+ mViewType = type;
}
}
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
index 6e0a300..32625ae 100644
--- a/tools/layoutlib/create/README.txt
+++ b/tools/layoutlib/create/README.txt
@@ -119,8 +119,8 @@
The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but updates the
references in all classes. This is used to update the references of classes in the java package that
-were added in the Dalvik VM but are not a part of the standard JVM. The existing classes are
-modified to update all references to these non-standard classes. An alternate implementation of
+were added in the Dalvik VM but are not a part of the Desktop VM. The existing classes are
+modified to update all references to these non-desktop classes. An alternate implementation of
these (com.android.tools.layoutlib.java.*) is injected.
RenameClassAdapter and RefactorClassAdapter both inherit from AbstractClassAdapter which changes the
@@ -130,11 +130,15 @@
valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on
Mac has horrible font rendering support.
+ReplaceMethodCallsAdapter replaces calls to certain methods. Currently, it only rewrites calls to
+java.lang.System.arraycopy([CI[CII)V, which is not part of the Desktop VM to call the more general
+method java.lang.System.arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V.
+
The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7
Transformation chains in the asm user guide, link in the References.) The order of execution of
these is:
ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] ->
-RefactorClassAdapter -> ClassWriter
+RefactorClassAdapter -> [ReplaceMethodCallsAdapter] -> ClassWriter
- Method stubs
--------------
@@ -169,7 +173,7 @@
- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new classes are
injected. The implementation for these classes has been taken from Android's libcore
(platform/libcore/luni/src/main/java/java/...).
-- Charsets, IntegralToString and UnsafeByteSequence are not part of the standard JAVA VM. They are
+- Charsets, IntegralToString and UnsafeByteSequence are not part of the Desktop VM. They are
added to the Dalvik VM for performance reasons. An implementation that is very close to the
original (which is at platform/libcore/luni/src/main/java/...) is injected. Since these classees
were in part of the java package, where we can't inject classes, all references to these have been
@@ -209,7 +213,7 @@
This is very similar to the Renaming classes except that it also updates the reference in all
classes. This is done for classes which are added to the Dalvik VM for performance reasons but are
-not present in the Standard Java VM. An implementation for these classes is also injected.
+not present in the Desktop VM. An implementation for these classes is also injected.
5- Method erasure based on return type
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index 3e75c9e..8373e30 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -32,6 +32,7 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -63,7 +64,8 @@
private final Set<String> mExcludedClasses;
/** Glob patterns of files to keep as is. */
private final String[] mIncludeFileGlobs;
- /** Copy these files into the output as is. */
+ /** Internal names of classes that contain method calls that need to be rewritten. */
+ private final Set<String> mReplaceMethodCallClasses = new HashSet<String>();
/**
* Creates a new analyzer.
@@ -109,6 +111,7 @@
mGen.setKeep(found);
mGen.setDeps(deps);
mGen.setCopyFiles(filesFound);
+ mGen.setRewriteMethodCallClasses(mReplaceMethodCallClasses);
}
}
@@ -118,7 +121,7 @@
*
* @param classes The map of class name => ASM ClassReader. Class names are
* in the form "android.view.View".
- * @param fileFound The map of file name => InputStream. The file name is
+ * @param filesFound The map of file name => InputStream. The file name is
* in the form "android/data/dataFile".
*/
void parseZip(List<String> jarPathList, Map<String, ClassReader> classes,
@@ -143,8 +146,8 @@
String className = classReaderToClassName(cr);
classes.put(className, cr);
} else {
- for (int i = 0; i < includeFilePatterns.length; ++i) {
- if (includeFilePatterns[i].matcher(entry.getName()).matches()) {
+ for (Pattern includeFilePattern : includeFilePatterns) {
+ if (includeFilePattern.matcher(entry.getName()).matches()) {
filesFound.put(entry.getName(), zip.getInputStream(entry));
break;
}
@@ -321,6 +324,7 @@
deps, new_deps);
for (ClassReader cr : inOutKeepClasses.values()) {
+ visitor.setClassName(cr.getClassName());
cr.accept(visitor, 0 /* flags */);
}
@@ -367,6 +371,8 @@
/** New classes to keep as-is found by this visitor. */
private final Map<String, ClassReader> mOutKeep;
+ private String mClassName;
+
/**
* Creates a new visitor that will find all the dependencies for the visited class.
* Types which are already in the zipClasses, keepClasses or inDeps are not marked.
@@ -390,6 +396,10 @@
mOutDeps = outDeps;
}
+ private void setClassName(String className) {
+ mClassName = className;
+ }
+
/**
* Considers the given class name as a dependency.
* If it does, add to the mOutDeps map.
@@ -429,7 +439,7 @@
// - android classes are added to dependencies
// - non-android classes are added to the list of classes to keep as-is (they don't need
// to be stubbed).
- if (className.indexOf("android") >= 0) { // TODO make configurable
+ if (className.contains("android")) { // TODO make configurable
mOutDeps.put(className, cr);
} else {
mOutKeep.put(className, cr);
@@ -594,7 +604,7 @@
// type and exceptions do not use generic types.
considerSignature(signature);
- return new MyMethodVisitor();
+ return new MyMethodVisitor(mClassName);
}
@Override
@@ -614,8 +624,11 @@
private class MyMethodVisitor extends MethodVisitor {
- public MyMethodVisitor() {
+ private String mOwnerClass;
+
+ public MyMethodVisitor(String ownerClass) {
super(Opcodes.ASM4);
+ mOwnerClass = ownerClass;
}
@@ -709,6 +722,13 @@
considerName(owner);
// desc is the method's descriptor (see Type).
considerDesc(desc);
+
+
+ // Check if method is java.lang.System.arrayCopy([CI[CII)V
+ if (owner.equals("java/lang/System") && name.equals("arraycopy")
+ && desc.equals("([CI[CII)V")) {
+ mReplaceMethodCallClasses.add(mOwnerClass);
+ }
}
// instruction multianewarray, whatever that is
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 207d8ae..c96a143 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -21,7 +21,6 @@
import org.objectweb.asm.ClassWriter;
import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -55,6 +54,8 @@
private Map<String, ClassReader> mDeps;
/** All files that are to be copied as-is. */
private Map<String, InputStream> mCopyFiles;
+ /** All classes where certain method calls need to be rewritten. */
+ private Set<String> mReplaceMethodCallsClasses;
/** Counter of number of classes renamed during transform. */
private int mRenameCount;
/** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */
@@ -133,7 +134,7 @@
assert i + 1 < n;
String oldFqcn = binaryToInternalClassName(refactorClasses[i]);
String newFqcn = binaryToInternalClassName(refactorClasses[i + 1]);
- mRefactorClasses.put(oldFqcn, newFqcn);;
+ mRefactorClasses.put(oldFqcn, newFqcn);
}
// create the map of renamed class -> return type of method to delete.
@@ -203,23 +204,12 @@
mCopyFiles = copyFiles;
}
- /** Gets the map of classes to output as-is, except if they have native methods */
- public Map<String, ClassReader> getKeep() {
- return mKeep;
- }
-
- /** Gets the map of dependencies that must be completely stubbed */
- public Map<String, ClassReader> getDeps() {
- return mDeps;
- }
-
- /** Gets the map of files to output as-is. */
- public Map<String, InputStream> getCopyFiles() {
- return mCopyFiles;
+ public void setRewriteMethodCallClasses(Set<String> rewriteMethodCallClasses) {
+ mReplaceMethodCallsClasses = rewriteMethodCallClasses;
}
/** Generates the final JAR */
- public void generate() throws FileNotFoundException, IOException {
+ public void generate() throws IOException {
TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
for (Class<?> clazz : mInjectClasses) {
@@ -329,14 +319,14 @@
String newName = transformName(className);
// transformName returns its input argument if there's no need to rename the class
- if (newName != className) {
+ if (!newName.equals(className)) {
mRenameCount++;
// This class is being renamed, so remove it from the list of classes not renamed.
mClassesNotRenamed.remove(className);
}
mLog.debug("Transform %s%s%s%s", className,
- newName == className ? "" : " (renamed to " + newName + ")",
+ newName.equals(className) ? "" : " (renamed to " + newName + ")",
hasNativeMethods ? " -- has natives" : "",
stubNativesOnly ? " -- stub natives only" : "");
@@ -344,8 +334,14 @@
// original class reader.
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- ClassVisitor cv = new RefactorClassAdapter(cw, mRefactorClasses);
- if (newName != className) {
+ ClassVisitor cv = cw;
+
+ if (mReplaceMethodCallsClasses.contains(className)) {
+ cv = new ReplaceMethodCallsAdapter(cv);
+ }
+
+ cv = new RefactorClassAdapter(cv, mRefactorClasses);
+ if (!newName.equals(className)) {
cv = new RenameClassAdapter(cv, className, newName);
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index 2e952fc..ad10656 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -193,8 +193,7 @@
private static boolean processArgs(Log log, String[] args,
ArrayList<String> osJarPath, String[] osDestJar) {
boolean needs_dest = true;
- for (int i = 0; i < args.length; i++) {
- String s = args[i];
+ for (String s : args) {
if (s.equals("-v")) {
log.setVerbose(true);
} else if (s.equals("-p")) {
@@ -212,7 +211,7 @@
osJarPath.add(s);
}
} else {
- log.error("Unknow argument: %s", s);
+ log.error("Unknown argument: %s", s);
return false;
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
new file mode 100644
index 0000000..e57eba1
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Replaces calls to certain methods that do not exist in the Desktop VM.
+ */
+public class ReplaceMethodCallsAdapter extends ClassVisitor {
+ public ReplaceMethodCallsAdapter(ClassVisitor cv) {
+ super(Opcodes.ASM4, cv);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ return new MyMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
+ }
+
+ private class MyMethodVisitor extends MethodVisitor {
+
+ public MyMethodVisitor(MethodVisitor mv) {
+ super(Opcodes.ASM4, mv);
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ // Check if method is java.lang.System.arrayCopy([CI[CII)V
+ if (owner.equals("java/lang/System") && name.equals("arraycopy")
+ && desc.equals("([CI[CII)V")) {
+ desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+ }
+ super.visitMethodInsn(opcode, owner, name, desc);
+ }
+ }
+}
diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py
index aea3241..bd9b14c 100755
--- a/tools/layoutlib/rename_font/build_font.py
+++ b/tools/layoutlib/rename_font/build_font.py
@@ -27,7 +27,7 @@
from fontTools import ttx
import re
import os
-from lxml import etree
+import xml.etree.ElementTree as etree
import shutil
import glob
from multiprocessing import Pool
@@ -84,14 +84,13 @@
ttx.main(ttx_args)
# now parse the xml file to change its PS name.
tree = etree.parse(ttx_path)
- encoding = tree.docinfo.encoding
root = tree.getroot()
for name in root.iter('name'):
[old_ps_name, version] = get_font_info(name)
if old_ps_name is not None and version is not None:
new_ps_name = old_ps_name + version
update_name(name, new_ps_name)
- tree.write(ttx_path, xml_declaration=True, encoding=encoding )
+ tree.write(ttx_path, xml_declaration=True, encoding='utf-8' )
# generate the udpated font now.
ttx_args = ['-q', '-d', dest_dir, ttx_path]
ttx.main(ttx_args)
diff --git a/tools/layoutlib/rename_font/test.py b/tools/layoutlib/rename_font/test.py
index d4c86cb..b0b69d8 100755
--- a/tools/layoutlib/rename_font/test.py
+++ b/tools/layoutlib/rename_font/test.py
@@ -14,7 +14,7 @@
from fontTools import ttx
import os
-from lxml import etree
+import xml.etree.ElementTree as etree
import shutil
import tempfile
diff --git a/wifi/java/android/net/wifi/RssiPacketCountInfo.java b/wifi/java/android/net/wifi/RssiPacketCountInfo.java
index f549e1d..0de2033 100644
--- a/wifi/java/android/net/wifi/RssiPacketCountInfo.java
+++ b/wifi/java/android/net/wifi/RssiPacketCountInfo.java
@@ -34,14 +34,17 @@
public int txbad;
+ public int rxgood;
+
public RssiPacketCountInfo() {
- rssi = txgood = txbad = 0;
+ rssi = txgood = txbad = rxgood = 0;
}
private RssiPacketCountInfo(Parcel in) {
rssi = in.readInt();
txgood = in.readInt();
txbad = in.readInt();
+ rxgood = in.readInt();
}
@Override
@@ -49,6 +52,7 @@
out.writeInt(rssi);
out.writeInt(txgood);
out.writeInt(txbad);
+ out.writeInt(rxgood);
}
@Override
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 99151c3..8191edd 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -89,6 +89,21 @@
* {@hide}
*/
public final static int UNSPECIFIED = -1;
+ /**
+ * @hide
+ * TODO: makes real freq boundaries
+ */
+ public boolean is24GHz() {
+ return frequency > 2400 && frequency < 2500;
+ }
+
+ /**
+ * @hide
+ * TODO: makes real freq boundaries
+ */
+ public boolean is5GHz() {
+ return frequency > 4900 && frequency < 5900;
+ }
/** information element from beacon
* @hide
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 5dfc318..b64ad60 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -342,14 +342,79 @@
/**
* @hide
+ * Uid of app owning the BSSID
+ */
+ public int bssidOwnerUid;
+
+ /**
+ * @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;
+ /** The Below RSSI thresholds are used to configure AutoJoin
+ * - GOOD/LOW/BAD thresholds are used so as to calculate link score
+ * - UNWANTED_SOFT are used by the blacklisting logic so as to handle the unwanted network message coming from CS
+ * - UNBLACKLIST thresholds are used so as to tweak the speed at which the network is unblacklisted (i.e. if
+ * it is seen with good RSSI, it is blacklisted faster)
+ * - INITIAL_AUTOJOIN_ATTEMPT, used to determine how close from the network we need to be before autojoin kicks in
+ */
/** @hide **/
public static int INVALID_RSSI = -127;
+ /** @hide **/
+ public static int UNWANTED_BLACKLIST_SOFT_RSSI_24 = -80;
+
+ /** @hide **/
+ public static int UNWANTED_BLACKLIST_SOFT_RSSI_5 = -70;
+
+ /** @hide **/
+ public static int GOOD_RSSI_24 = -65;
+
+ /** @hide **/
+ public static int LOW_RSSI_24 = -75;
+
+ /** @hide **/
+ public static int BAD_RSSI_24 = -85;
+
+ /** @hide **/
+ public static int GOOD_RSSI_5 = -55;
+
+ /** @hide **/
+ public static int LOW_RSSI_5 = -65;
+
+ /** @hide **/
+ public static int BAD_RSSI_5 = -75;
+
+ /** @hide **/
+ public static int UNWANTED_BLACKLIST_SOFT_BUMP = 4;
+
+ /** @hide **/
+ public static int UNWANTED_BLACKLIST_HARD_BUMP = 8;
+
+ /** @hide **/
+ public static int UNBLACKLIST_THRESHOLD_24_SOFT = -75;
+
+ /** @hide **/
+ public static int UNBLACKLIST_THRESHOLD_24_HARD = -68;
+
+ /** @hide **/
+ public static int UNBLACKLIST_THRESHOLD_5_SOFT = -63;
+
+ /** @hide **/
+ public static int UNBLACKLIST_THRESHOLD_5_HARD = -56;
+
+ /** @hide **/
+ public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_24 = -80;
+
+ /** @hide **/
+ public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_5 = -70;
+
+ /** @hide
+ * 5GHz band is prefered over 2.4 if the 5GHz RSSI is higher than this threshold **/
+ public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65;
+
/**
* @hide
* A summary of the RSSI and Band status for that configuration
@@ -426,11 +491,11 @@
if (result.seen == 0)
continue;
- if ((result.frequency > 4900) && (result.frequency < 5900)) {
+ if (result.is5GHz()) {
//strictly speaking: [4915, 5825]
//number of known BSSID on 5GHz band
status.num5 = status.num5 + 1;
- } else if ((result.frequency > 2400) && (result.frequency < 2500)) {
+ } else if (result.is24GHz()) {
//strictly speaking: [2412, 2482]
//number of known BSSID on 2.4Ghz band
status.num24 = status.num24 + 1;
@@ -438,12 +503,12 @@
if ((now_ms - result.seen) > age) continue;
- if ((result.frequency > 4900) && (result.frequency < 5900)) {
+ if (result.is5GHz()) {
if (result.level > status.rssi5) {
status.rssi5 = result.level;
status.age5 = result.seen;
}
- } else if ((result.frequency > 2400) && (result.frequency < 2500)) {
+ } else if (result.is24GHz()) {
if (result.level > status.rssi24) {
status.rssi24 = result.level;
status.age24 = result.seen;
@@ -456,7 +521,7 @@
/** @hide */
public static final int AUTO_JOIN_ENABLED = 0;
- /** @hide
+ /**
* if this is set, the WifiConfiguration cannot use linkages so as to bump
* it's relative priority.
* - status between and 128 indicate various level of blacklisting depending
@@ -465,7 +530,17 @@
* although it may have been self added we will not re-self-add it, ignore it,
* not return it to applications, and not connect to it
* */
+
+ /** @hide
+ * network was temporary disabled due to bad connection, most likely due
+ * to weak RSSI */
public static final int AUTO_JOIN_TEMPORARY_DISABLED = 1;
+ /** @hide
+ * network was temporary disabled due to bad connection, which cant be attributed
+ * to weak RSSI */
+ public static final int AUTO_JOIN_TEMPORARY_DISABLED_LINK_ERRORS = 32;
+ /** @hide */
+ public static final int AUTO_JOIN_TEMPORARY_DISABLED_AT_SUPPLICANT = 64;
/** @hide */
public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE = 128;
/** @hide */
@@ -476,6 +551,24 @@
*/
public int autoJoinStatus;
+
+ /**
+ * @hide
+ */
+ public long blackListTimestamp;
+
+ /**
+ * @hide
+ * last time the system was connected to this configuration.
+ */
+ public long lastConnected;
+
+ /**
+ * @hide
+ * last time the system was disconnected to this configuration.
+ */
+ public long lastDisconnected;
+
/**
* Set if the configuration was self added by the framework
* This boolean is cleared if we get a connect/save/ update or
@@ -586,7 +679,20 @@
// TODO: Add more checks
return true;
+ }
+ /**
+ * Helper function, identify if a configuration is linked
+ * @hide
+ */
+ public boolean isLinked(WifiConfiguration config) {
+ if (config.linkedConfigurations != null && linkedConfigurations != null) {
+ if (config.linkedConfigurations.get(configKey()) != null
+ && linkedConfigurations.get(config.configKey()) != null) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -614,6 +720,17 @@
return mostRecent;
}
+ /** @hide **/
+ public void setAutoJoinStatus(int status) {
+ if (status < 0) status = 0;
+ if (status == 0) {
+ blackListTimestamp = 0;
+ } else if (status > autoJoinStatus) {
+ blackListTimestamp = System.currentTimeMillis();
+ }
+ autoJoinStatus = status;
+ }
+
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
@@ -697,6 +814,15 @@
if (selfAdded) sbuf.append("selfAdded");
if (creatorUid != 0) sbuf.append("uid=" + Integer.toString(creatorUid));
+ if (blackListTimestamp != 0) {
+ long now_ms = System.currentTimeMillis();
+ long diff = now_ms - blackListTimestamp;
+ if (diff <= 0) {
+ sbuf.append("blackListed since <incorrect>");
+ } else {
+ sbuf.append("blackListed since ").append(Long.toString(diff/1000)).append( "sec");
+ }
+ }
return sbuf.toString();
}
@@ -985,8 +1111,12 @@
didSelfAdd = source.didSelfAdd;
lastConnectUid = source.lastConnectUid;
lastUpdateUid = source.lastUpdateUid;
+ bssidOwnerUid = source.bssidOwnerUid;
creatorUid = source.creatorUid;
peerWifiConfiguration = source.peerWifiConfiguration;
+ blackListTimestamp = source.blackListTimestamp;
+ lastConnected = source.lastConnected;
+ lastDisconnected = source.lastDisconnected;
}
}
@@ -1030,17 +1160,8 @@
dest.writeInt(creatorUid);
dest.writeInt(lastConnectUid);
dest.writeInt(lastUpdateUid);
- /*
- TODO: should we write the cache results to the parcel?
- if (scanResultCache != null) {
- dest.writeInt(WifiConfiguration.SCAN_CACHE_TAG);
- dest.writeInt(scanResultCache.size());
- for (ScanResult result : scanResultCache.values()) {
- result.writeToParcel(dest, flags);
- }
- } else {
- dest.writeInt(WifiConfiguration.NOTHING_TAG);
- }*/
+ dest.writeInt(bssidOwnerUid);
+ dest.writeLong(blackListTimestamp);
}
/** Implement the Parcelable interface {@hide} */
@@ -1079,26 +1200,8 @@
config.creatorUid = in.readInt();
config.lastConnectUid = in.readInt();
config.lastUpdateUid = in.readInt();
- /*
- TODO: should we write the cache results to the parcel?
- boolean done = false;
- do {
- int tag = in.readInt();
- switch (tag) {
- case WifiConfiguration.SCAN_CACHE_TAG:
- int size = in.readInt();
- config.scanResultCache = new HashMap<String, ScanResult>();
- while (size > 0) {
- ScanResult result = ScanResult.CREATOR.createFromParcel(in);
- config.scanResultCache.put(result.BSSID, result);
- size--;
- }
- break;
- case WifiConfiguration.NOTHING_TAG:
- done = true;
- break;
- }
- } while (!done);*/
+ config.bssidOwnerUid = in.readInt();
+ config.blackListTimestamp = in.readLong();
return config;
}
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index f44cb0a..54a7df2 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -40,7 +40,7 @@
* of <code>DetailedState</code>.
*/
private static final EnumMap<SupplicantState, DetailedState> stateMap =
- new EnumMap<SupplicantState, DetailedState>(SupplicantState.class);
+ new EnumMap<SupplicantState, DetailedState>(SupplicantState.class);
static {
stateMap.put(SupplicantState.DISCONNECTED, DetailedState.DISCONNECTED);
@@ -62,14 +62,31 @@
private String mBSSID;
private WifiSsid mWifiSsid;
private int mNetworkId;
- /** Received Signal Strength Indicator */
+
+ /** @hide **/
+ public static final int INVALID_RSSI = -127;
+
+ /** @hide **/
+ public static final int MIN_RSSI = -126;
+
+ /** @hide **/
+ public static final int MAX_RSSI = 200;
+
+
+ /**
+ * Received Signal Strength Indicator
+ */
private int mRssi;
- /** Link speed in Mbps */
+ /**
+ * Link speed in Mbps
+ */
public static final String LINK_SPEED_UNITS = "Mbps";
private int mLinkSpeed;
- /** Frequency in MHz */
+ /**
+ * Frequency in MHz
+ */
public static final String FREQUENCY_UNITS = "MHz";
private int mFrequency;
@@ -77,9 +94,116 @@
private String mMacAddress;
/**
- * Flag indicating that AP has hinted that upstream connection is metered,
- * and sensitive to heavy data transfers.
+ * @hide
*/
+ public long txBad;
+ /**
+ * @hide
+ */
+ public long txRetries;
+ /**
+ * @hide
+ */
+ public long txSuccess;
+ /**
+ * @hide
+ */
+ public long rxSuccess;
+ /**
+ * @hide
+ */
+ public double txBadRate;
+ /**
+ * @hide
+ */
+ public double txRetriesRate;
+ /**
+ * @hide
+ */
+ public double txSuccessRate;
+ /**
+ * @hide
+ */
+ public double rxSuccessRate;
+
+ /**
+ * @hide
+ */
+ public int badRssiCount;
+
+ /**
+ * @hide
+ */
+ public int lowRssiCount;
+
+ /**
+ * @hide
+ */
+ public int score;
+
+ /**
+ * TODO: get actual timestamp and calculate true rates
+ * @hide
+ */
+ public void updatePacketRates(WifiLinkLayerStats stats) {
+ if (stats != null) {
+ long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo;
+ long txretries = stats.retries_be + stats.retries_bk
+ + stats.retries_vi + stats.retries_vo;
+ long rxgood = stats.rxmpdu_be + stats.rxmpdu_bk + stats.rxmpdu_vi + stats.rxmpdu_vo;
+ long txbad = stats.lostmpdu_be + stats.lostmpdu_bk
+ + stats.lostmpdu_vi + stats.lostmpdu_vo;
+
+ txBadRate = (txBadRate * 0.5)
+ + ((double) (txbad - txBad) * 0.5);
+ txSuccessRate = (txSuccessRate * 0.5)
+ + ((double) (txgood - txSuccess) * 0.5);
+ rxSuccessRate = (rxSuccessRate * 0.5)
+ + ((double) (rxgood - rxSuccess) * 0.5);
+ txRetriesRate = (txRetriesRate * 0.5)
+ + ((double) (txretries - txRetries) * 0.5);
+
+ txBad = txbad;
+ txSuccess = txgood;
+ rxSuccess = rxgood;
+ txRetries = txretries;
+ } else {
+ txBad = 0;
+ txSuccess = 0;
+ rxSuccess = 0;
+ txRetries = 0;
+ txBadRate = 0;
+ txSuccessRate = 0;
+ rxSuccessRate = 0;
+ txRetriesRate = 0;
+ }
+ }
+
+
+ /**
+ * This function is less powerful and used if the WifiLinkLayerStats API is not implemented
+ * at the Wifi HAL
+ * @hide
+ */
+ public void updatePacketRates(long txPackets, long rxPackets) {
+ //paranoia
+ txBad = 0;
+ txRetries = 0;
+ txBadRate = 0;
+ txRetriesRate = 0;
+
+ txSuccessRate = (txSuccessRate * 0.5)
+ + ((double) (txPackets - txSuccess) * 0.5);
+ rxSuccessRate = (rxSuccessRate * 0.5)
+ + ((double) (rxPackets - rxSuccess) * 0.5);
+ txSuccess = txPackets;
+ rxSuccess = rxPackets;
+ }
+
+ /**
+ * Flag indicating that AP has hinted that upstream connection is metered,
+ * and sensitive to heavy data transfers.
+ */
private boolean mMeteredHint;
/** @hide */
@@ -88,11 +212,34 @@
mBSSID = null;
mNetworkId = -1;
mSupplicantState = SupplicantState.UNINITIALIZED;
- mRssi = -9999;
+ mRssi = INVALID_RSSI;
mLinkSpeed = -1;
mFrequency = -1;
}
+ /** @hide */
+ public void reset() {
+ setInetAddress(null);
+ setBSSID(null);
+ setSSID(null);
+ setNetworkId(-1);
+ setRssi(INVALID_RSSI);
+ setLinkSpeed(-1);
+ setFrequency(-1);
+ setMeteredHint(false);
+ txBad = 0;
+ txSuccess = 0;
+ rxSuccess = 0;
+ txRetries = 0;
+ txBadRate = 0;
+ txSuccessRate = 0;
+ rxSuccessRate = 0;
+ txRetriesRate = 0;
+ lowRssiCount = 0;
+ badRssiCount = 0;
+ score = 0;
+ }
+
/**
* Copy constructor
* @hide
@@ -109,6 +256,17 @@
mIpAddress = source.mIpAddress;
mMacAddress = source.mMacAddress;
mMeteredHint = source.mMeteredHint;
+ txBad = source.txBad;
+ txRetries = source.txRetries;
+ txSuccess = source.txSuccess;
+ rxSuccess = source.rxSuccess;
+ txBadRate = source.txBadRate;
+ txRetriesRate = source.txRetriesRate;
+ txSuccessRate = source.txSuccessRate;
+ rxSuccessRate = source.rxSuccessRate;
+ score = source.score;
+ badRssiCount = source.badRssiCount;
+ lowRssiCount = source.lowRssiCount;
}
}
@@ -158,7 +316,7 @@
/**
* Returns the received signal strength indicator of the current 802.11
* network, in dBm.
- * @return the RSSI, in the range -110 to 10
+ * @return the RSSI, in the range -127 to 200
*/
public int getRssi() {
return mRssi;
@@ -166,6 +324,10 @@
/** @hide */
public void setRssi(int rssi) {
+ if (rssi < INVALID_RSSI)
+ rssi = INVALID_RSSI;
+ if (rssi > MAX_RSSI)
+ rssi = MAX_RSSI;
mRssi = rssi;
}
@@ -198,6 +360,22 @@
}
/**
+ * @hide
+ * TODO: makes real freq boundaries
+ */
+ public boolean is24GHz() {
+ return mFrequency < 4000;
+ }
+
+ /**
+ * @hide
+ * TODO: makes real freq boundaries
+ */
+ public boolean is5GHz() {
+ return mFrequency > 4000;
+ }
+
+ /**
* Record the MAC address of the WLAN interface
* @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form
* @hide
@@ -325,8 +503,8 @@
append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS).
append(", Frequency: ").append(mFrequency).append(FREQUENCY_UNITS).
append(", Net ID: ").append(mNetworkId).
- append(", Metered hint: ").append(mMeteredHint);
-
+ append(", Metered hint: ").append(mMeteredHint).
+ append(", score: ").append(Integer.toString(score));
return sb.toString();
}
@@ -356,6 +534,13 @@
dest.writeString(mBSSID);
dest.writeString(mMacAddress);
dest.writeInt(mMeteredHint ? 1 : 0);
+ dest.writeInt(score);
+ dest.writeDouble(txSuccessRate);
+ dest.writeDouble(txRetriesRate);
+ dest.writeDouble(txBadRate);
+ dest.writeDouble(rxSuccessRate);
+ dest.writeInt(badRssiCount);
+ dest.writeInt(lowRssiCount);
mSupplicantState.writeToParcel(dest, flags);
}
@@ -379,6 +564,13 @@
info.mBSSID = in.readString();
info.mMacAddress = in.readString();
info.mMeteredHint = in.readInt() != 0;
+ info.score = in.readInt();
+ info.txSuccessRate = in.readDouble();
+ info.txRetriesRate = in.readDouble();
+ info.txBadRate = in.readDouble();
+ info.rxSuccessRate = in.readDouble();
+ info.badRssiCount = in.readInt();
+ info.lowRssiCount = in.readInt();
info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
return info;
}
diff --git a/wifi/java/android/net/wifi/WifiLinkLayerStats.java b/wifi/java/android/net/wifi/WifiLinkLayerStats.java
index 922eddd..ae2fa98 100644
--- a/wifi/java/android/net/wifi/WifiLinkLayerStats.java
+++ b/wifi/java/android/net/wifi/WifiLinkLayerStats.java
@@ -111,6 +111,8 @@
/** {@hide} */
public String toString() {
StringBuilder sbuf = new StringBuilder();
+ sbuf.append(" WifiLinkLayerStats: ").append('\n');
+
if (this.SSID != null) {
sbuf.append(" SSID: ").append(this.SSID).append('\n');
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 3b65ca8..21b700d 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -153,12 +153,17 @@
dest.writeInt(band);
dest.writeInt(periodInMs);
dest.writeInt(reportEvents);
- dest.writeInt(channels.length);
- for (int i = 0; i < channels.length; i++) {
- dest.writeInt(channels[i].frequency);
- dest.writeInt(channels[i].dwellTimeMS);
- dest.writeInt(channels[i].passive ? 1 : 0);
+ if (channels != null) {
+ dest.writeInt(channels.length);
+
+ for (int i = 0; i < channels.length; i++) {
+ dest.writeInt(channels[i].frequency);
+ dest.writeInt(channels[i].dwellTimeMS);
+ dest.writeInt(channels[i].passive ? 1 : 0);
+ }
+ } else {
+ dest.writeInt(0);
}
}
@@ -211,10 +216,14 @@
/** Implement the Parcelable interface {@hide} */
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mResults.length);
- for (int i = 0; i < mResults.length; i++) {
- ScanResult result = mResults[i];
- result.writeToParcel(dest, flags);
+ if (mResults != null) {
+ dest.writeInt(mResults.length);
+ for (int i = 0; i < mResults.length; i++) {
+ ScanResult result = mResults[i];
+ result.writeToParcel(dest, flags);
+ }
+ } else {
+ dest.writeInt(0);
}
}
@@ -324,13 +333,17 @@
dest.writeInt(unchangedSampleSize);
dest.writeInt(minApsBreachingThreshold);
dest.writeInt(periodInMs);
- dest.writeInt(hotspotInfos.length);
- for (int i = 0; i < hotspotInfos.length; i++) {
- HotspotInfo info = hotspotInfos[i];
- dest.writeString(info.bssid);
- dest.writeInt(info.low);
- dest.writeInt(info.high);
- dest.writeInt(info.frequencyHint);
+ if (hotspotInfos != null) {
+ dest.writeInt(hotspotInfos.length);
+ for (int i = 0; i < hotspotInfos.length; i++) {
+ HotspotInfo info = hotspotInfos[i];
+ dest.writeString(info.bssid);
+ dest.writeInt(info.low);
+ dest.writeInt(info.high);
+ dest.writeInt(info.frequencyHint);
+ }
+ } else {
+ dest.writeInt(0);
}
}
@@ -456,13 +469,18 @@
/** Implement the Parcelable interface {@hide} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(apLostThreshold);
- dest.writeInt(hotspotInfos.length);
- for (int i = 0; i < hotspotInfos.length; i++) {
- HotspotInfo info = hotspotInfos[i];
- dest.writeString(info.bssid);
- dest.writeInt(info.low);
- dest.writeInt(info.high);
- dest.writeInt(info.frequencyHint);
+
+ if (hotspotInfos != null) {
+ dest.writeInt(hotspotInfos.length);
+ for (int i = 0; i < hotspotInfos.length; i++) {
+ HotspotInfo info = hotspotInfos[i];
+ dest.writeString(info.bssid);
+ dest.writeInt(info.low);
+ dest.writeInt(info.high);
+ dest.writeInt(info.frequencyHint);
+ }
+ } else {
+ dest.writeInt(0);
}
}
@@ -680,6 +698,42 @@
}
}
+ /** @hide */
+ public static class OperationResult implements Parcelable {
+ public int reason;
+ public String description;
+
+ public OperationResult(int reason, String description) {
+ this.reason = reason;
+ this.description = description;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(reason);
+ dest.writeString(description);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<OperationResult> CREATOR =
+ new Creator<OperationResult>() {
+ public OperationResult createFromParcel(Parcel in) {
+ int reason = in.readInt();
+ String description = in.readString();
+ return new OperationResult(reason, description);
+ }
+
+ public OperationResult[] newArray(int size) {
+ return new OperationResult[size];
+ }
+ };
+ }
+
private static class ServiceHandler extends Handler {
ServiceHandler(Looper looper) {
super(looper);
@@ -717,9 +771,11 @@
case CMD_OP_SUCCEEDED :
((ActionListener) listener).onSuccess();
break;
- case CMD_OP_FAILED :
- ((ActionListener) listener).onFailure(msg.arg1, (String)msg.obj);
- removeListener(msg.arg2);
+ case CMD_OP_FAILED : {
+ OperationResult result = (OperationResult)msg.obj;
+ ((ActionListener) listener).onFailure(result.reason, result.description);
+ removeListener(msg.arg2);
+ }
break;
case CMD_SCAN_RESULT :
((ScanListener) listener).onResults(
diff --git a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
index 8375d09..61c2b8a 100644
--- a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
+++ b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
@@ -16,6 +16,8 @@
package android.net.wifi.passpoint;
+import android.net.wifi.ScanResult;
+import android.net.wifi.passpoint.WifiPasspointPolicy;
import android.os.Messenger;
/**
@@ -27,5 +29,6 @@
{
Messenger getMessenger();
int getPasspointState();
+ List<WifiPasspointPolicy> requestCredentialMatch(in List<ScanResult> requested);
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
index 5ef1bf9..aec8797 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
@@ -197,27 +197,13 @@
}
public static class CellularNetwork {
- public byte[] rawData;
-
- public int getMnc() {
- // TODO
- return 0;
- }
-
- public int getMcc() {
- // TODO
- return 0;
- }
+ public String mcc;
+ public String mnc;
@Override
public String toString() {
- if (rawData == null) return null;
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < rawData.length; i++)
- sb.append(String.format("%02X", rawData[i]));
- return sb.toString();
+ return mcc + "," + mnc;
}
-
}
/** BSSID */
@@ -239,7 +225,7 @@
public List<NaiRealm> naiRealm;
/** 3GPP cellular network */
- public CellularNetwork cellularNetwork;
+ public List<CellularNetwork> cellularNetwork;
/** fully qualified domain name (FQDN) */
public List<String> domainName;
@@ -300,10 +286,11 @@
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("BSSID: ").append(bssid);
+ sb.append("BSSID: ").append("(").append(bssid).append(")");
if (venueName != null)
- sb.append(" venueName: ").append(venueName.replace("\n", "\\n"));
+ sb.append(" venueName: ").append("(")
+ .append(venueName.replace("\n", "\\n")).append(")");
if (networkAuthType != null) {
sb.append(" networkAuthType: ");
@@ -328,9 +315,11 @@
sb.append("(").append(realm.toString()).append(")");
}
- if (cellularNetwork != null)
- sb.append(" cellularNetwork: ").append("(")
- .append(cellularNetwork.toString()).append(")");
+ if (cellularNetwork != null) {
+ sb.append(" cellularNetwork: ");
+ for (CellularNetwork plmn : cellularNetwork)
+ sb.append("(").append(plmn.toString()).append(")");
+ }
if (domainName != null) {
sb.append(" domainName: ");
@@ -404,8 +393,11 @@
if (cellularNetwork == null) {
out.writeInt(0);
} else {
- out.writeInt(cellularNetwork.rawData.length);
- out.writeByteArray(cellularNetwork.rawData);
+ out.writeInt(cellularNetwork.size());
+ for (CellularNetwork plmn : cellularNetwork) {
+ out.writeString(plmn.mcc);
+ out.writeString(plmn.mnc);
+ }
}
@@ -505,9 +497,13 @@
n = in.readInt();
if (n > 0) {
- p.cellularNetwork = new CellularNetwork();
- p.cellularNetwork.rawData = new byte[n];
- in.readByteArray(p.cellularNetwork.rawData);
+ p.cellularNetwork = new ArrayList<CellularNetwork>();
+ for (int i = 0; i < n; i++) {
+ CellularNetwork plmn = new CellularNetwork();
+ plmn.mcc = in.readString();
+ plmn.mnc = in.readString();
+ p.cellularNetwork.add(plmn);
+ }
}
n = in.readInt();
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index 55acbad..2f158c2 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -22,6 +22,8 @@
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
@@ -45,58 +47,53 @@
/* Passpoint states values */
- /** Passpoint is in an known state. This should only occur in boot time @hide */
+ /** Passpoint is in an unknown state. This should only occur in boot time */
public static final int PASSPOINT_STATE_UNKNOWN = 0;
- /** Passpoint is disabled. This occurs when wifi is disabled. @hide */
+ /** Passpoint is disabled. This occurs when wifi is disabled */
public static final int PASSPOINT_STATE_DISABLED = 1;
- /** Passpoint is enabled and in discovery state. @hide */
+ /** Passpoint is enabled and in discovery state */
public static final int PASSPOINT_STATE_DISCOVERY = 2;
- /** Passpoint is enabled and in access state. @hide */
+ /** Passpoint is enabled and in access state */
public static final int PASSPOINT_STATE_ACCESS = 3;
- /** Passpoint is enabled and in provisioning state. @hide */
+ /** Passpoint is enabled and in provisioning state */
public static final int PASSPOINT_STATE_PROVISION = 4;
/* Passpoint callback error codes */
- /** Indicates that the operation failed due to an internal error @hide */
- public static final int ERROR = 0;
+ /** Indicates that the operation failed due to an internal error */
+ public static final int REASON_ERROR = 0;
- /** Indicates that the operation failed because wifi is disabled @hide */
- public static final int WIFI_DISABLED = 1;
+ /** Indicates that the operation failed because wifi is disabled */
+ public static final int REASON_WIFI_DISABLED = 1;
- /** Indicates that the operation failed because the framework is busy @hide */
- public static final int BUSY = 2;
+ /** Indicates that the operation failed because the framework is busy */
+ public static final int REASON_BUSY = 2;
+
+ /** Indicates that the operation failed because parameter is invalid */
+ public static final int REASON_INVALID_PARAMETER = 3;
+
+ /** Indicates that the operation failed because the server is not trusted */
+ public static final int REASON_NOT_TRUSTED = 4;
/**
* protocol supported for Passpoint
- * @hide
*/
public static final String PROTOCOL_DM = "OMA-DM-ClientInitiated";
/**
* protocol supported for Passpoint
- * @hide
*/
public static final String PROTOCOL_SOAP = "SPP-ClientInitiated";
/* Passpoint broadcasts */
/**
- * Broadcast intent action indicating that Passpoint online sign up is
- * avaiable.
- * @hide
- */
- public static final String PASSPOINT_OSU_AVAILABLE =
- "android.net.wifi.passpoint.OSU_AVAILABLE";
-
- /**
* Broadcast intent action indicating that the state of Passpoint
* connectivity has changed
- * @hide
*/
public static final String PASSPOINT_STATE_CHANGED_ACTION =
"android.net.wifi.passpoint.STATE_CHANGE";
@@ -104,7 +101,6 @@
/**
* Broadcast intent action indicating that the saved Passpoint credential
* list has changed
- * @hide
*/
public static final String PASSPOINT_CRED_CHANGED_ACTION =
"android.net.wifi.passpoint.CRED_CHANGE";
@@ -112,21 +108,18 @@
/**
* Broadcast intent action indicating that Passpoint online sign up is
* avaiable.
- * @hide
*/
public static final String PASSPOINT_OSU_AVAILABLE_ACTION =
"android.net.wifi.passpoint.OSU_AVAILABLE";
/**
* Broadcast intent action indicating that user remediation is required
- * @hide
*/
public static final String PASSPOINT_USER_REM_REQ_ACTION =
"android.net.wifi.passpoint.USER_REM_REQ";
/**
* Interface for callback invocation when framework channel is lost
- * @hide
*/
public interface ChannelListener {
/**
@@ -138,14 +131,13 @@
/**
* Interface for callback invocation on an application action
- * @hide
*/
public interface ActionListener {
/** The operation succeeded */
public void onSuccess();
/**
- * * The operation failed
+ * The operation failed
*
* @param reason The reason for failure could be one of
* {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY}
@@ -155,7 +147,6 @@
/**
* Interface for callback invocation when doing OSU or user remediation
- * @hide
*/
public interface OsuRemListener {
/** The operation succeeded */
@@ -171,11 +162,11 @@
/**
* Browser launch is requried for user interaction. When this callback
- * is called, app should launch browser / webview to the given URL.
+ * is called, app should launch browser / webview to the given URI.
*
- * @param url URL for browser launch
+ * @param uri URI for browser launch
*/
- public void onBrowserLaunch(String url);
+ public void onBrowserLaunch(String uri);
/**
* When this is called, app should dismiss the previously lanched browser.
@@ -187,7 +178,6 @@
* A channel that connects the application to the wifi passpoint framework.
* Most passpoint operations require a Channel as an argument.
* An instance of Channel is obtained by doing a call on {@link #initialize}
- * @hide
*/
public static class Channel {
private final static int INVALID_LISTENER_KEY = 0;
@@ -288,7 +278,8 @@
@Override
public void handleMessage(Message message) {
- Object listener = getListener(message.arg2, false);
+ Object listener = null;
+
switch (message.what) {
case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
if (mChannelListener != null) {
@@ -300,6 +291,7 @@
case REQUEST_ANQP_INFO_SUCCEEDED:
WifiPasspointInfo result = (WifiPasspointInfo) message.obj;
anqpRequestFinish(result);
+ listener = getListener(message.arg2, false);
if (listener != null) {
((ActionListener) listener).onSuccess();
}
@@ -307,6 +299,7 @@
case REQUEST_ANQP_INFO_FAILED:
anqpRequestFinish((ScanResult) message.obj);
+ listener = getListener(message.arg2, false);
if (listener == null)
getListener(message.arg2, true);
if (listener != null) {
@@ -314,6 +307,31 @@
}
break;
+ case START_OSU_SUCCEEDED:
+ listener = getListener(message.arg2, true);
+ if (listener != null) {
+ ((OsuRemListener) listener).onSuccess();
+ }
+ break;
+
+ case START_OSU_FAILED:
+ listener = getListener(message.arg2, true);
+ if (listener != null) {
+ ((OsuRemListener) listener).onFailure(message.arg1);
+ }
+ break;
+
+ case START_OSU_BROWSER:
+ listener = getListener(message.arg2, true);
+ if (listener != null) {
+ ParcelableString str = (ParcelableString) message.obj;
+ if (str.string == null)
+ ((OsuRemListener) listener).onBrowserDismiss();
+ else
+ ((OsuRemListener) listener).onBrowserLaunch(str.string);
+ }
+ break;
+
default:
Log.d(TAG, "Ignored " + message);
break;
@@ -323,25 +341,46 @@
}
+ public static class ParcelableString implements Parcelable {
+ public String string;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(string);
+ }
+
+ public static final Parcelable.Creator<ParcelableString> CREATOR =
+ new Parcelable.Creator<ParcelableString>() {
+ @Override
+ public ParcelableString createFromParcel(Parcel in) {
+ ParcelableString ret = new ParcelableString();
+ ret.string = in.readString();
+ return ret;
+ }
+ @Override
+ public ParcelableString[] newArray(int size) {
+ return new ParcelableString[size];
+ }
+ };
+ }
+
private static final int BASE = Protocol.BASE_WIFI_PASSPOINT_MANAGER;
- /** @hide */
- public static final int REQUEST_ANQP_INFO = BASE + 1;
-
- /** @hide */
- public static final int REQUEST_ANQP_INFO_FAILED = BASE + 2;
-
- /** @hide */
- public static final int REQUEST_ANQP_INFO_SUCCEEDED = BASE + 3;
-
- /** @hide */
- public static final int REQUEST_OSU_INFO = BASE + 4;
-
- /** @hide */
- public static final int REQUEST_OSU_INFO_FAILED = BASE + 5;
-
- /** @hide */
- public static final int REQUEST_OSU_INFO_SUCCEEDED = BASE + 6;
+ public static final int REQUEST_ANQP_INFO = BASE + 1;
+ public static final int REQUEST_ANQP_INFO_FAILED = BASE + 2;
+ public static final int REQUEST_ANQP_INFO_SUCCEEDED = BASE + 3;
+ public static final int REQUEST_OSU_ICON = BASE + 4;
+ public static final int REQUEST_OSU_ICON_FAILED = BASE + 5;
+ public static final int REQUEST_OSU_ICON_SUCCEEDED = BASE + 6;
+ public static final int START_OSU = BASE + 7;
+ public static final int START_OSU_BROWSER = BASE + 8;
+ public static final int START_OSU_FAILED = BASE + 9;
+ public static final int START_OSU_SUCCEEDED = BASE + 10;
private Context mContext;
IWifiPasspointManager mService;
@@ -350,7 +389,6 @@
* TODO: doc
* @param context
* @param service
- * @hide
*/
public WifiPasspointManager(Context context, IWifiPasspointManager service) {
mContext = context;
@@ -368,7 +406,6 @@
* @return Channel instance that is necessary for performing any further
* passpoint operations
*
- * @hide
*/
public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
Messenger messenger = getMessenger();
@@ -387,8 +424,6 @@
/**
* STOPSHIP: temp solution, should use supplicant manager instead, check
* with b/13931972
- *
- * @hide
*/
public Messenger getMessenger() {
try {
@@ -398,7 +433,6 @@
}
}
- /** @hide */
public int getPasspointState() {
try {
return mService.getPasspointState();
@@ -407,7 +441,6 @@
}
}
- /** @hide */
public void requestAnqpInfo(Channel c, List<ScanResult> requested, int mask,
ActionListener listener) {
Log.d(TAG, "requestAnqpInfo start");
@@ -434,14 +467,16 @@
Log.d(TAG, "requestAnqpInfo end");
}
- /** @hide */
public void requestOsuIcons(Channel c, List<WifiPasspointOsuProvider> requested,
int resolution, ActionListener listener) {
}
- /** @hide */
public List<WifiPasspointPolicy> requestCredentialMatch(List<ScanResult> requested) {
- return null;
+ try {
+ return mService.requestCredentialMatch(requested);
+ } catch (RemoteException e) {
+ return null;
+ }
}
/**
@@ -486,21 +521,21 @@
return true;
}
- /** @hide */
- public void startOsu(Channel c, WifiPasspointOsuProvider selected, OsuRemListener listener) {
-
+ public void startOsu(Channel c, WifiPasspointOsuProvider osu, OsuRemListener listener) {
+ Log.d(TAG, "startOsu start");
+ checkChannel(c);
+ int key = c.putListener(listener);
+ c.mAsyncChannel.sendMessage(START_OSU, 0, key, osu);
+ Log.d(TAG, "startOsu end");
}
- /** @hide */
public void startUserRemediation(Channel c, OsuRemListener listener) {
}
- /** @hide */
- public void connect(WifiPasspointPolicy selected) {
+ public void connect(WifiPasspointPolicy policy) {
}
private static void checkChannel(Channel c) {
- if (c == null)
- throw new IllegalArgumentException("Channel needs to be initialized");
+ if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
}
}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
index f40dc4f..b54b70c 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
@@ -87,12 +87,12 @@
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
- sb.append("SSID: ").append(ssid);
+ sb.append("SSID: ").append("<").append(ssid).append(">");
if (friendlyName != null)
- sb.append(" friendlyName: ").append(friendlyName);
+ sb.append(" friendlyName: ").append("<").append(friendlyName).append(">");
if (serverUri != null)
- sb.append(" serverUri: ").append(serverUri);
- sb.append(" osuMethod: ").append(osuMethod);
+ sb.append(" serverUri: ").append("<").append(serverUri).append(">");
+ sb.append(" osuMethod: ").append("<").append(osuMethod).append(">");
if (iconFileName != null) {
sb.append(" icon: <").append(iconWidth).append("x")
.append(iconHeight).append(" ")
@@ -100,9 +100,9 @@
.append(iconFileName).append(">");
}
if (osuNai != null)
- sb.append(" osuNai: ").append(osuNai);
+ sb.append(" osuNai: ").append("<").append(osuNai).append(">");
if (osuService != null)
- sb.append(" osuService: ").append(osuService);
+ sb.append(" osuService: ").append("<").append(osuService).append(">");
return sb.toString();
}